clankerbend 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DEVELOPERS.md +6 -6
- package/README.md +31 -14
- package/apps/README.md +3 -3
- package/apps/sticky-notes/README.md +2 -2
- package/apps/sticky-notes/{onewhack.manifest.json → clankerbend.manifest.json} +1 -1
- package/apps/sticky-notes/package.json +3 -3
- package/apps/sticky-notes/public/app.js +42 -9
- package/apps/sticky-notes/src/sticky-notes-app.js +17 -21
- package/apps/vim-nav/README.md +22 -22
- package/apps/vim-nav/{onewhack.manifest.json → clankerbend.manifest.json} +4 -4
- package/apps/vim-nav/package.json +3 -3
- package/apps/vim-nav/public/app.js +44 -11
- package/apps/vim-nav/public/index.html +2 -2
- package/apps/vim-nav/server.mjs +2 -2
- package/apps/vim-nav/src/vim-nav-app.js +5 -5
- package/assets/clankerbend-banner.webp +0 -0
- package/assets/clankerbend-demo-thumbnail.webp +0 -0
- package/assets/clankerbend.svg +26 -0
- package/cli.mjs +22 -11
- package/docs/app-lifecycle.md +9 -9
- package/docs/app-manifest.md +4 -4
- package/docs/author-guide.md +5 -5
- package/docs/launcher-profiles.md +45 -10
- package/docs/protocol.md +248 -241
- package/host/README.md +4 -4
- package/host/src/app-registry.js +8 -6
- package/host/src/codex-desktop-cdp-adapter.js +85 -65
- package/host/src/codex-desktop-renderer-bridge.js +839 -270
- package/host/src/index.js +186 -34
- package/launch/accounts.mjs +360 -0
- package/launch/codex-mux-adapter.mjs +175 -0
- package/launch/profiles.mjs +40 -16
- package/launch/runtime-paths.mjs +9 -6
- package/launch/test-accounts.mjs +71 -0
- package/package.json +12 -10
- package/scripts/release-npm.mjs +1 -2
- package/server.mjs +21 -14
- package/assets/onewhack.jpg +0 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { ClankerBendHost, createMockTranscriptAdapter } from "../host/src/index.js";
|
|
6
|
+
import { CodexAccountRegistry, PRIMARY_ACCOUNT_ID } from "./accounts.mjs";
|
|
7
|
+
|
|
8
|
+
const root = mkdtempSync(join(tmpdir(), "clankerbend-accounts-test-"));
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const primaryHome = join(root, "primary-codex-home");
|
|
12
|
+
const primaryElectronProfile = join(root, "primary-electron-profile");
|
|
13
|
+
mkdirSync(primaryHome, { recursive: true });
|
|
14
|
+
mkdirSync(primaryElectronProfile, { recursive: true });
|
|
15
|
+
writeFileSync(join(primaryHome, "config.toml"), "model = \"gpt-5.4\"\n");
|
|
16
|
+
writeFileSync(join(primaryHome, "auth.json"), JSON.stringify({ auth_mode: "chatgpt", tokens: {} }));
|
|
17
|
+
|
|
18
|
+
const registry = new CodexAccountRegistry({
|
|
19
|
+
root: join(root, "state"),
|
|
20
|
+
primaryCodexHome: primaryHome,
|
|
21
|
+
primaryElectronProfile
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const listed = registry.list();
|
|
25
|
+
assert.equal(listed.accounts.length, 1);
|
|
26
|
+
assert.equal(listed.accounts[0].id, PRIMARY_ACCOUNT_ID);
|
|
27
|
+
assert.equal(listed.accounts[0].codexHome, primaryHome);
|
|
28
|
+
|
|
29
|
+
const work = registry.createManaged({ id: "work", label: "Work" });
|
|
30
|
+
assert.equal(work.id, "work");
|
|
31
|
+
assert.equal(existsSync(work.codexHome), true);
|
|
32
|
+
assert.equal(existsSync(work.electronProfile), true);
|
|
33
|
+
assert.equal(readFileSync(join(work.codexHome, "config.toml"), "utf8"), "cli_auth_credentials_store = \"file\"\n");
|
|
34
|
+
|
|
35
|
+
const duplicateLabel = registry.createManaged({ label: "Work" });
|
|
36
|
+
assert.equal(duplicateLabel.id, "work-2");
|
|
37
|
+
const numericLabel = registry.createManaged({ label: "123" });
|
|
38
|
+
assert.equal(numericLabel.id, "account-123");
|
|
39
|
+
|
|
40
|
+
writeFileSync(join(work.codexHome, "auth.json"), JSON.stringify({ auth_mode: "chatgpt", tokens: { account: "work" } }));
|
|
41
|
+
const adoption = registry.adoptAsPrimary("work");
|
|
42
|
+
assert.equal(existsSync(primaryHome), true);
|
|
43
|
+
assert.equal(JSON.parse(readFileSync(join(primaryHome, "auth.json"), "utf8")).tokens.account, "work");
|
|
44
|
+
assert.equal(adoption.previousPrimary.kind, "managed");
|
|
45
|
+
assert.equal(existsSync(adoption.backup.codexHome), true);
|
|
46
|
+
assert.equal(registry.list().accounts.some((account) => account.id === "work"), false);
|
|
47
|
+
|
|
48
|
+
const rollback = registry.rollbackPrimary({ backupId: adoption.previousPrimary.id });
|
|
49
|
+
assert.equal(rollback.restoredAccountId, adoption.previousPrimary.id);
|
|
50
|
+
assert.equal(JSON.parse(readFileSync(join(primaryHome, "auth.json"), "utf8")).tokens.account, undefined);
|
|
51
|
+
assert.equal(registry.list().accounts.some((account) => account.id === rollback.replacedPrimary.id), true);
|
|
52
|
+
|
|
53
|
+
const temp = registry.createManaged({ id: "temp", label: "Temp" });
|
|
54
|
+
const deleted = registry.deleteManaged("temp");
|
|
55
|
+
assert.equal(registry.list().accounts.some((account) => account.id === "temp"), false);
|
|
56
|
+
assert.equal(existsSync(deleted.deletedRoot), true);
|
|
57
|
+
assert.equal(existsSync(deleted.codexHome), true);
|
|
58
|
+
|
|
59
|
+
const fresh = registry.createManaged({ id: "fresh", label: "Fresh" });
|
|
60
|
+
const host = new ClankerBendHost({
|
|
61
|
+
accountRegistry: registry,
|
|
62
|
+
transcriptAdapter: createMockTranscriptAdapter()
|
|
63
|
+
});
|
|
64
|
+
assert.equal(host.publicState().codexAccounts.accounts.find((account) => account.id === "fresh").auth.authJson, false);
|
|
65
|
+
writeFileSync(join(fresh.codexHome, "auth.json"), JSON.stringify({ auth_mode: "chatgpt", tokens: { account: "fresh" } }));
|
|
66
|
+
assert.equal(host.publicState().codexAccounts.accounts.find((account) => account.id === "fresh").auth.authJson, true);
|
|
67
|
+
|
|
68
|
+
console.log("clankerbend account registry tests passed");
|
|
69
|
+
} finally {
|
|
70
|
+
rmSync(root, { recursive: true, force: true });
|
|
71
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clankerbend",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "OneWill
|
|
5
|
+
"description": "OneWill ClankerBend protocol host and public reference apps.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"codex",
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
"desktop",
|
|
11
11
|
"extensions"
|
|
12
12
|
],
|
|
13
|
-
"homepage": "https://github.com/OneWillAI/
|
|
13
|
+
"homepage": "https://github.com/OneWillAI/clankerbend#readme",
|
|
14
14
|
"bugs": {
|
|
15
|
-
"url": "https://github.com/OneWillAI/
|
|
15
|
+
"url": "https://github.com/OneWillAI/clankerbend/issues"
|
|
16
16
|
},
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/OneWillAI/
|
|
19
|
+
"url": "git+https://github.com/OneWillAI/clankerbend.git"
|
|
20
20
|
},
|
|
21
21
|
"bin": {
|
|
22
22
|
"clankerbend": "./cli.mjs"
|
|
@@ -24,17 +24,19 @@
|
|
|
24
24
|
"files": [
|
|
25
25
|
"cli.mjs",
|
|
26
26
|
"server.mjs",
|
|
27
|
-
"assets/
|
|
27
|
+
"assets/clankerbend-banner.webp",
|
|
28
|
+
"assets/clankerbend-demo-thumbnail.webp",
|
|
29
|
+
"assets/clankerbend.svg",
|
|
28
30
|
"scripts/release-npm.mjs",
|
|
29
31
|
"host/src",
|
|
30
32
|
"launch",
|
|
31
33
|
"apps/README.md",
|
|
32
|
-
"apps/vim-nav/
|
|
34
|
+
"apps/vim-nav/clankerbend.manifest.json",
|
|
33
35
|
"apps/vim-nav/package.json",
|
|
34
36
|
"apps/vim-nav/public",
|
|
35
37
|
"apps/vim-nav/src",
|
|
36
38
|
"apps/vim-nav/README.md",
|
|
37
|
-
"apps/sticky-notes/
|
|
39
|
+
"apps/sticky-notes/clankerbend.manifest.json",
|
|
38
40
|
"apps/sticky-notes/package.json",
|
|
39
41
|
"apps/sticky-notes/public",
|
|
40
42
|
"apps/sticky-notes/src",
|
|
@@ -49,8 +51,8 @@
|
|
|
49
51
|
"apps/*"
|
|
50
52
|
],
|
|
51
53
|
"scripts": {
|
|
52
|
-
"start": "node cli.mjs
|
|
53
|
-
"start:mock": "node cli.mjs
|
|
54
|
+
"start": "node cli.mjs",
|
|
55
|
+
"start:mock": "node cli.mjs --mock",
|
|
54
56
|
"test": "node scripts/test.mjs",
|
|
55
57
|
"test:vim-nav": "npm --prefix apps/vim-nav test",
|
|
56
58
|
"test:sticky-notes": "npm --prefix apps/sticky-notes test",
|
package/scripts/release-npm.mjs
CHANGED
|
@@ -95,7 +95,7 @@ function isKnownProvenanceCi() {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
async function repackWithName(tarball, packageName) {
|
|
98
|
-
const stageRoot = await mkdtemp(join(tmpdir(), "
|
|
98
|
+
const stageRoot = await mkdtemp(join(tmpdir(), "clankerbend-npm-release-"));
|
|
99
99
|
try {
|
|
100
100
|
await execFileAsync("tar", ["-xzf", tarball, "-C", stageRoot], { cwd: ROOT });
|
|
101
101
|
const packageDir = join(stageRoot, "package");
|
|
@@ -195,7 +195,6 @@ function splitCompoundName(part) {
|
|
|
195
195
|
const tokens = [part];
|
|
196
196
|
if (part.includes("clanker")) tokens.push("clanker");
|
|
197
197
|
if (part.includes("bend")) tokens.push("bend");
|
|
198
|
-
if (part.includes("whack")) tokens.push("whack");
|
|
199
198
|
if (part.includes("onewill")) tokens.push("onewill");
|
|
200
199
|
if (part.includes("ai")) tokens.push("ai");
|
|
201
200
|
return tokens;
|
package/server.mjs
CHANGED
|
@@ -1,31 +1,38 @@
|
|
|
1
1
|
import { pathToFileURL } from "node:url";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { ClankerBendHost, createMockTranscriptAdapter } from "./host/src/index.js";
|
|
3
|
+
import { createCodexDesktopMuxAdapter } from "./launch/codex-mux-adapter.mjs";
|
|
4
4
|
import { navigateProfile, printLaunchStatus } from "./launch/profiles.mjs";
|
|
5
5
|
|
|
6
|
-
export async function
|
|
6
|
+
export async function launchClankerBendCodex(options = {}) {
|
|
7
7
|
const mockMode = Boolean(options.mock);
|
|
8
|
-
const localDevInsecure = options.localDevInsecure === true || process.env.
|
|
8
|
+
const localDevInsecure = options.localDevInsecure === true || process.env.ONEWILL_CLANKERBEND_DISABLE_AUTH === "1";
|
|
9
9
|
const profile = await navigateProfile({
|
|
10
10
|
stateDir: options.stateDir,
|
|
11
11
|
runDir: options.runDir,
|
|
12
12
|
registryConfigPath: options.registryConfigPath,
|
|
13
|
-
registryProfileId: options.registryProfileId
|
|
13
|
+
registryProfileId: options.registryProfileId,
|
|
14
|
+
accountId: options.accountId,
|
|
15
|
+
primaryCodexHome: options.primaryCodexHome
|
|
14
16
|
});
|
|
15
17
|
|
|
16
|
-
const host = new
|
|
18
|
+
const host = new ClankerBendHost({
|
|
17
19
|
hostId: profile.hostId,
|
|
18
20
|
hostName: profile.hostName,
|
|
19
|
-
token: options.token || process.env.
|
|
21
|
+
token: options.token || process.env.ONEWILL_CLANKERBEND_TOKEN || undefined,
|
|
20
22
|
localDevInsecure,
|
|
21
23
|
runDir: profile.runDir,
|
|
24
|
+
accountRegistry: profile.accountRegistry,
|
|
22
25
|
transcriptAdapter: mockMode
|
|
23
26
|
? createMockTranscriptAdapter({ defaultAppId: profile.defaultPanelAppId, providers: profile.providers })
|
|
24
|
-
:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
: createCodexDesktopMuxAdapter({
|
|
28
|
+
accountRegistry: profile.accountRegistry,
|
|
29
|
+
startAccountId: profile.startAccountId,
|
|
30
|
+
providers: profile.providers,
|
|
31
|
+
adapterOptions: {
|
|
32
|
+
runDir: profile.runDir,
|
|
33
|
+
rendererBridges: profile.rendererBridges,
|
|
34
|
+
providers: profile.providers
|
|
35
|
+
}
|
|
29
36
|
})
|
|
30
37
|
});
|
|
31
38
|
|
|
@@ -41,7 +48,7 @@ export async function launchOneWhackCodex(options = {}) {
|
|
|
41
48
|
process.once("SIGINT", () => cleanupAndExit(0));
|
|
42
49
|
process.once("SIGTERM", () => cleanupAndExit(0));
|
|
43
50
|
process.once("uncaughtException", (err) => {
|
|
44
|
-
console.error(`
|
|
51
|
+
console.error(`ClankerBend could not start. Close any ClankerBend-launched Codex Desktop window, then run this command again. Details: ${err.message}`);
|
|
45
52
|
cleanupAndExit(1);
|
|
46
53
|
});
|
|
47
54
|
}
|
|
@@ -52,7 +59,7 @@ export async function launchOneWhackCodex(options = {}) {
|
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
if (import.meta.url === pathToFileURL(process.argv[1] || "").href) {
|
|
55
|
-
await
|
|
62
|
+
await launchClankerBendCodex({
|
|
56
63
|
mock: process.argv.includes("--mock")
|
|
57
64
|
});
|
|
58
65
|
}
|
package/assets/onewhack.jpg
DELETED
|
Binary file
|