Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
6621252
refactor: Move sqlite-wasm code to extension
rekmarks Jan 18, 2025
196a7c1
Merge branch 'main' into rekm/sqlite-wasm-to-extension
grypez Jan 21, 2025
0102680
draft
grypez Jan 9, 2025
f55c9ce
fix vitest endoify resolution
grypez Jan 10, 2025
6df657b
align nodejs vat-worker with extension vat iframe
grypez Jan 11, 2025
9f034bb
draft
grypez Jan 14, 2025
13e860c
XtrEme uNiT tESTing
grypez Jan 22, 2025
935e700
Merge branch 'main' into grypez/nodejs-unit-tests
grypez Jan 22, 2025
2ecbebd
mock make-kernel.test kvStore
grypez Jan 22, 2025
ca85077
thresholds
grypez Jan 22, 2025
eea9684
give getPort test more time
grypez Jan 22, 2025
35a2137
Merge branch 'main' into grypez/nodejs-unit-tests
grypez Jan 22, 2025
b17eece
Merge branch 'main' into grypez/nodejs-unit-tests
grypez Jan 22, 2025
df25f8d
more refined
grypez Jan 22, 2025
a18237c
fix endo mocking
grypez Jan 23, 2025
5ce6c79
remove endoify from Kernel.ts
grypez Jan 23, 2025
4f7fdce
further e2e testing
grypez Jan 24, 2025
5ff625f
trace dispatch error
grypez Jan 24, 2025
2bd4b2c
get plucky
grypez Jan 24, 2025
196f216
cleanup
grypez Jan 24, 2025
9c257db
remove debug logs
grypez Jan 24, 2025
e1fc71d
more cleanup
grypez Jan 24, 2025
09db0f2
Merge branch 'main' into grypez/nodejs-unit-tests
grypez Jan 24, 2025
6cab757
return type test func
grypez Jan 24, 2025
73dc899
appease linter
grypez Jan 24, 2025
2b33baf
update extensions
grypez Jan 24, 2025
90c0d2a
thresholds
grypez Jan 24, 2025
82bb2bd
simplify test a bit
grypez Jan 24, 2025
d760a69
isolate CI failure
grypez Jan 24, 2025
a5b9154
test(nodejs): reallocate coverage between unit and e2e phases
grypez Jan 27, 2025
7fc4b8e
thresholds
grypez Jan 27, 2025
e5cc333
Merge branch 'main' into grypez/nodejs-unit-tests
grypez Jan 27, 2025
039a8f7
Merge branch 'main' into grypez/nodejs-unit-tests
grypez Jan 27, 2025
ad3ec78
Merge branch 'main' into grypez/nodejs-unit-tests
grypez Jan 27, 2025
dfe16ea
feat(nodejs): Add an ollama vat
grypez Jan 27, 2025
8556607
draft
grypez Jan 29, 2025
598e397
latest draft
grypez Jan 30, 2025
05df1c3
add ollama script and fix vats
grypez Jan 31, 2025
282591e
Merge branch 'main' into grypez/deepseek
grypez Jan 31, 2025
e3a5445
add vatPower maker to kernel
grypez Feb 3, 2025
e55ea58
yad
grypez Feb 3, 2025
847b1b6
a working rag demo
grypez Feb 4, 2025
ad79608
Alice & Bob: draft
grypez Feb 6, 2025
2ae278b
trim kernel reports
grypez Feb 11, 2025
df9983d
endow Alice, Bob & Eve w/ trust
grypez Feb 12, 2025
465a3dd
.gitignore nodejs bundle files
grypez Feb 14, 2025
83be0e7
yarn dedupe
grypez Feb 18, 2025
fb61f99
pull out some ancillary logic
grypez Feb 18, 2025
eb46907
clear kvStore before use
grypez Feb 18, 2025
c0c48e6
tidy up a little
grypez Feb 18, 2025
242cfb8
tidy up a lot
grypez Feb 23, 2025
ac9721d
a weak streaming console example
grypez Feb 25, 2025
36b576d
opt out of kernel logs
grypez Mar 10, 2025
45c8126
abstract vat-to-vat stream wrapper
grypez Mar 11, 2025
d5ee80a
misc
grypez Mar 12, 2025
9f33569
close to merge
grypez Mar 12, 2025
b6a5dee
drop better-sqlite3 dep from nodejs
grypez Mar 12, 2025
bbbb825
remove bundle-example
grypez Mar 12, 2025
a1af352
remove unused llm test
grypez Mar 12, 2025
d7105f8
lint
grypez Mar 12, 2025
20e3fcc
remove unused deps
grypez Mar 12, 2025
671ace2
fix e2e test
grypez Mar 12, 2025
01b658a
added auto-served version of demo:rag:ci
grypez Mar 13, 2025
8390d55
add but do not use ref~reader
grypez Mar 13, 2025
ee31097
moderninze console demo
grypez Mar 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,13 @@
"vite>sass>@parcel/watcher": false,
"vitest>@vitest/browser>webdriverio>@wdio/utils>edgedriver": false,
"vitest>@vitest/browser>webdriverio>@wdio/utils>geckodriver": false,
"vitest>@vitest/mocker>msw": false
"vitest>@vitest/mocker>msw": false,
"vite>tsx>esbuild": false
}
},
"resolutions": {
"cookie": "^0.7.0"
"cookie": "^0.7.0",
"form-data": "4.0.2",
"foreground-child": "3.1.1"
}
}
16 changes: 11 additions & 5 deletions packages/kernel/src/Kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ import {
} from './types.ts';
import { VatHandle } from './VatHandle.ts';

const VERBOSE = false;

/**
* Obtain the KRef from a simple value represented as a CapData object.
*
Expand All @@ -67,6 +69,10 @@ type MessageRoute = {
target: KRef;
} | null;

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const clip = (content: string, length = 10) =>
`${content.substring(0, length)}${content.length > length ? '...' : ''}`;

export class Kernel {
/** Command channel from the controlling console/browser extension/test driver */
readonly #commandStream: DuplexStream<KernelCommand, KernelCommandReply>;
Expand Down Expand Up @@ -506,16 +512,16 @@ export class Kernel {
* @param item - The message/notification to deliver.
*/
async #deliver(item: RunQueueItem): Promise<void> {
const { log } = console;
const log = VERBOSE ? console.log : (_: unknown) => undefined;
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const glimpse = (obj: unknown) => clip(JSON.stringify(obj));
switch (item.type) {
case 'send': {
const route = this.#routeMessage(item);
if (route) {
const { vatId, target } = route;
const { message } = item;
log(
`@@@@ deliver ${vatId} send ${target}<-${JSON.stringify(message)}`,
);
log(`@@@@ deliver ${vatId} send ${target}<-${glimpse(message)}`);
if (vatId) {
const vat = this.#getVat(vatId);
if (vat) {
Expand All @@ -534,7 +540,7 @@ export class Kernel {
} else {
this.#storage.enqueuePromiseMessage(target, message);
}
log(`@@@@ done ${vatId} send ${target}<-${JSON.stringify(message)}`);
log(`@@@@ done ${vatId} send ${target}<-${glimpse(message)}`);
}
break;
}
Expand Down
24 changes: 16 additions & 8 deletions packages/kernel/src/VatHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import type {
RunQueueItemSend,
} from './types.ts';

const VERBOSE = false;

type VatConstructorProps = {
kernel: Kernel;
vatId: VatId;
Expand All @@ -36,6 +38,10 @@ type VatConstructorProps = {
logger?: Logger | undefined;
};

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const clip = (content: string, length = 10) =>
`${content.substring(0, length)}${content.length > length ? '...' : ''}`;

export class VatHandle {
/** The ID of the vat this is the VatHandle for */
readonly vatId: VatId;
Expand Down Expand Up @@ -323,12 +329,14 @@ export class VatHandle {
const kso: VatSyscallObject = this.#translateSyscallVtoK(vso);
const [op] = kso;
const { vatId } = this;
const { log } = console;
const log = VERBOSE ? console.log : (_: unknown) => undefined;
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const glimpse = (obj: unknown) => clip(JSON.stringify(obj));
switch (op) {
case 'send': {
// [KRef, Message];
const [, target, message] = kso;
log(`@@@@ ${vatId} syscall send ${target}<-${JSON.stringify(message)}`);
log(`@@@@ ${vatId} syscall send ${target}<-${glimpse(message)}`);
this.#handleSyscallSend(target, message);
break;
}
Expand All @@ -342,38 +350,38 @@ export class VatHandle {
case 'resolve': {
// [VatOneResolution[]];
const [, resolutions] = kso;
log(`@@@@ ${vatId} syscall resolve ${JSON.stringify(resolutions)}`);
log(`@@@@ ${vatId} syscall resolve ${glimpse(resolutions)}`);
this.#handleSyscallResolve(resolutions as VatOneResolution[]);
break;
}
case 'exit': {
// [boolean, SwingSetCapData];
const [, fail, info] = kso;
log(`@@@@ ${vatId} syscall exit fail=${fail} ${JSON.stringify(info)}`);
log(`@@@@ ${vatId} syscall exit fail=${fail} ${glimpse(info)}`);
break;
}
case 'dropImports': {
// [KRef[]];
const [, refs] = kso;
log(`@@@@ ${vatId} syscall dropImports ${JSON.stringify(refs)}`);
log(`@@@@ ${vatId} syscall dropImports ${glimpse(refs)}`);
break;
}
case 'retireImports': {
// [KRef[]];
const [, refs] = kso;
log(`@@@@ ${vatId} syscall retireImports ${JSON.stringify(refs)}`);
log(`@@@@ ${vatId} syscall retireImports ${glimpse(refs)}`);
break;
}
case 'retireExports': {
// [KRef[]];
const [, refs] = kso;
log(`@@@@ ${vatId} syscall retireExports ${JSON.stringify(refs)}`);
log(`@@@@ ${vatId} syscall retireExports ${glimpse(refs)}`);
break;
}
case 'abandonExports': {
// [KRef[]];
const [, refs] = kso;
log(`@@@@ ${vatId} syscall abandonExports ${JSON.stringify(refs)}`);
log(`@@@@ ${vatId} syscall abandonExports ${glimpse(refs)}`);
break;
}
case 'callNow':
Expand Down
9 changes: 8 additions & 1 deletion packages/kernel/src/VatSupervisor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type SupervisorConstructorProps = {
id: VatId;
commandStream: DuplexStream<VatCommand, VatCommandReply>;
makeKVStore: MakeKVStore;
makePowers?: () => Promise<Record<string, unknown>>;
fetchBlob?: FetchBlob;
};

Expand All @@ -56,6 +57,9 @@ export class VatSupervisor {
/** Capability to create the store for this vat. */
readonly #makeKVStore: MakeKVStore;

/** An initialization routine for powers bestowed to this vat. */
readonly #makePowers: () => Promise<Record<string, unknown>>;

/** Capability to fetch the bundle of code to run in this vat. */
readonly #fetchBlob: FetchBlob;

Expand All @@ -69,17 +73,20 @@ export class VatSupervisor {
* @param params.id - The id of the vat being supervised.
* @param params.commandStream - Communications channel connected to the kernel.
* @param params.makeKVStore - Capability to create the store for this vat.
* @param params.makePowers - Capability to create powers this vat.
* @param params.fetchBlob - Function to fetch the user code bundle for this vat.
*/
constructor({
id,
commandStream,
makeKVStore,
makePowers,
fetchBlob,
}: SupervisorConstructorProps) {
this.id = id;
this.#commandStream = commandStream;
this.#makeKVStore = makeKVStore;
this.#makePowers = makePowers ?? (async () => ({}));
this.#dispatch = null;
const defaultFetchBlob: FetchBlob = async (bundleURL: string) =>
fetch(bundleURL);
Expand Down Expand Up @@ -219,7 +226,7 @@ export class VatSupervisor {
`[vat-${this.id}]`,
);
const syscall = makeSupervisorSyscall(this, kvStore);
const vatPowers = {}; // XXX should be something more real
const vatPowers = await this.#makePowers();
const liveSlotsOptions = {}; // XXX should be something more real

const gcTools: GCTools = harden({
Expand Down
17 changes: 17 additions & 0 deletions packages/nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
"build:docs": "typedoc",
"changelog:validate": "../../scripts/validate-changelog.sh @ocap/nodejs",
"clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist",
"demo:rag": "node ./dist/demo/rag/run.mjs",
"demo:rag:ci": "./scripts/demo-rag-ci.sh",
"demo:console": "node ./dist/demo/console/run.mjs",
"demo:console:ci": "./scripts/demo-console-ci.sh",
"lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies",
"lint:dependencies": "depcheck",
"lint:eslint": "eslint . --cache",
Expand All @@ -38,12 +42,25 @@
"test:watch": "vitest --config vitest.config.ts"
},
"dependencies": {
"@endo/base64": "^1.0.9",
"@endo/eventual-send": "^1.2.6",
"@endo/exo": "^1.5.8",
"@endo/far": "^1.1.9",
"@endo/marshal": "^1.6.2",
"@endo/pass-style": "^1.4.7",
"@endo/patterns": "^1.4.8",
"@endo/promise-kit": "^1.1.6",
"@endo/stream": "^1.2.6",
"@langchain/core": "^0.3.37",
"@langchain/ollama": "^0.1.5",
"@langchain/textsplitters": "^0.1.0",
"@ocap/kernel": "workspace:^",
"@ocap/shims": "workspace:^",
"@ocap/store": "workspace:^",
"@ocap/streams": "workspace:^",
"@ocap/utils": "workspace:^",
"langchain": "^0.3.15",
"ollama": "^0.5.12",
"ses": "^1.9.0"
},
"devDependencies": {
Expand Down
101 changes: 101 additions & 0 deletions packages/nodejs/scripts/call-ollama.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { readFile } from 'fs/promises';
import ollama from 'ollama';

/**
* Streams the response from Ollama to the console.
*
* @param {*} response - The response from Ollama.
*/
async function streamResponse(response) {
const thinkEndToken = '</think>';
const thinkLabel = 'OLLAMA Thought';
const thinkingDots = ['.', '..', '...'];
const dotInterval = 400;
let thinkingDotsIndex = 0;
let thinking = true;
let accumulatedContent = '';
const thinkingInterval = setInterval(() => {
process.stdout.clearLine();
process.stdout.write(`OLLAMA Thinking${thinkingDots[thinkingDotsIndex]}\r`);
thinkingDotsIndex += 1;
thinkingDotsIndex %= thinkingDots.length;
}, dotInterval);
console.time(thinkLabel);
for await (const part of response) {
accumulatedContent += part.message.content;
if (thinking) {
if (accumulatedContent.includes(thinkEndToken)) {
process.stdout.clearLine();
console.timeEnd(thinkLabel);
const tail = accumulatedContent.split(thinkEndToken)[1];
process.stdout.write(`OLLAMA Response: ${tail}`);
clearInterval(thinkingInterval);
thinking = false;
}
} else {
process.stdout.write(part.message.content); // Write each part of the response to the console
}
}
}

const getFileContent = async (path) => {
const resolvedPath = new URL(path, import.meta.url).pathname;
return (await readFile(resolvedPath)).toString();
};

/**
* The main function for the script.
*
* @param {*} param0 - An arguments bag.
* @param { string } param0.model - The model to pull and use.
* @param { string } param0.prompt - The prompt to give the model.
*/
async function main({ model, prompt }) {
if (!prompt) {
throw new Error('say something');
}

console.log('OLLAMA', 'pull');

await ollama.pull({ model });

console.log('USER:', prompt);

const response = await ollama.chat({
model, // Specify the model you want to use
messages: [
{
role: 'admin',
content: [
`You are an instance of LLM model ${model}.`,
`Respond to user requests ${'respectfully'} and ${'informatively'}.`,
].join(' '),
},
{
role: 'admin',
content: [
'The following is the raw content of the wikipedia page titled "ambient authority".',
await getFileContent('./ambient-authority.txt'),
].join('\n\n'),
},
{
role: 'admin',
content: [
'The following is the raw content of the wikipedia page titled "confused deputy problem".',
await getFileContent('./confused-deputy-problem.txt'),
].join('\n\n'),
},
{ role: 'user', content: prompt },
], // The message to send
stream: true, // Enable streaming
});

await streamResponse(response).catch(console.error);
console.log('\n'); // Add a newline after the streaming response
}

const model = 'deepseek-r1:1.5b';

const [, , prompt] = process.argv;

main({ model, prompt }).catch(console.error);
23 changes: 23 additions & 0 deletions packages/nodejs/scripts/demo-console-ci.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env bash

set -x
set -e
set -o pipefail

# We borrow the vat definition from extension for now
yarn ocap bundle "src/demo/console/vats"

# Start the server in background and capture its PID
yarn ocap serve "src/demo/console/vats" &
SERVER_PID=$!

function cleanup() {
# Kill the server if it's still running
if kill -0 $SERVER_PID 2>/dev/null; then
kill $SERVER_PID
fi
}
# Ensure we always close the server
trap cleanup EXIT

yarn demo:console
23 changes: 23 additions & 0 deletions packages/nodejs/scripts/demo-rag-ci.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env bash

set -x
set -e
set -o pipefail

# We borrow the vat definition from extension for now
yarn ocap bundle "src/demo/rag/vats"

# Start the server in background and capture its PID
yarn ocap serve "src/demo/rag/vats" &
SERVER_PID=$!

function cleanup() {
# Kill the server if it's still running
if kill -0 $SERVER_PID 2>/dev/null; then
kill $SERVER_PID
fi
}
# Ensure we always close the server
trap cleanup EXIT

yarn demo:rag
Loading
Loading