@kybernesis/create 0.20.0 → 0.22.0
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/dist/add.js +6 -0
- package/dist/buzz.d.ts +29 -0
- package/dist/buzz.js +154 -0
- package/dist/cli.js +1 -0
- package/dist/doctor.js +2 -0
- package/dist/model-reach.d.ts +2 -0
- package/dist/model-reach.js +16 -1
- package/dist/templates.js +7 -5
- package/dist/upgrade.js +6 -13
- package/package.json +1 -1
- package/skills/kybernesis-packages/SKILL.md +2 -2
package/dist/add.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { basename, join, resolve } from "node:path";
|
|
3
3
|
import { bold, dim, green, red, run, yellow } from "./util.js";
|
|
4
|
+
import { addBuzz } from "./buzz.js";
|
|
4
5
|
import { CHANNEL_KINDS, arcanaMemorySlotTs, channelPlan } from "./templates.js";
|
|
5
6
|
/**
|
|
6
7
|
* `kyb add channel <kind>` — put another surface on an agent that already exists.
|
|
@@ -21,10 +22,15 @@ import { CHANNEL_KINDS, arcanaMemorySlotTs, channelPlan } from "./templates.js";
|
|
|
21
22
|
export async function add(what, rest) {
|
|
22
23
|
if (what === "memory")
|
|
23
24
|
return addMemory();
|
|
25
|
+
// Buzz is a bridge and a key, not a channel file; "add channel buzz" is what
|
|
26
|
+
// the provisioner and people naturally type, so both spellings land here.
|
|
27
|
+
if (what === "buzz" || (what === "channel" && rest.find((a) => !a.startsWith("-")) === "buzz"))
|
|
28
|
+
return addBuzz();
|
|
24
29
|
if (what !== "channel") {
|
|
25
30
|
console.error(red(`kyb add: don't know how to add "${what ?? ""}".`));
|
|
26
31
|
console.log(dim(` kyb add channel <${CHANNEL_KINDS.filter((k) => k !== "none").join("|")}>`));
|
|
27
32
|
console.log(dim(` kyb add memory ${dim("— an eve 0.49 memory slot backed by Arcana (recall before every turn)")}`));
|
|
33
|
+
console.log(dim(` kyb add buzz ${dim("— the agent's own Buzz identity, tools and bridge, ready for its owner's community")}`));
|
|
28
34
|
process.exitCode = 1;
|
|
29
35
|
return;
|
|
30
36
|
}
|
package/dist/buzz.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `kyb add buzz` — get an agent ready for its owner's Buzz community.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Buzz is not a channel file. The agent is a member of the community with its
|
|
6
|
+
* own key, a separate bridge process hears mentions and answers as the person
|
|
7
|
+
* who spoke, and an extension gives the agent the rest of the workspace. Every
|
|
8
|
+
* one of those used to be a manual step on the host, so ticking Buzz on a build
|
|
9
|
+
* did nothing at all.
|
|
10
|
+
*
|
|
11
|
+
* This does everything that does not depend on the customer: the package, the
|
|
12
|
+
* extension mount, the agent's own key, the Buzz CLI and the bridge's unit. The
|
|
13
|
+
* unit is left switched off, because the one thing only the customer can give
|
|
14
|
+
* is which community it lives in. That arrives later through the owner's
|
|
15
|
+
* Channels page (manage's `buzz.join`), which writes the relay, publishes the
|
|
16
|
+
* agent's profile and starts the bridge.
|
|
17
|
+
*/
|
|
18
|
+
export declare const BUZZ_PACKAGE = "@kybernesis/buzz@^0.10.2";
|
|
19
|
+
export declare const BUZZ_EXTENSION_MOUNT: string;
|
|
20
|
+
/** Where the agent's Buzz key lives: inside the agent, so the bridge and the agent read the same file. */
|
|
21
|
+
export declare function buzzKeyPath(appDir: string): string;
|
|
22
|
+
export declare function buzzBridgeUnitName(agent: string): string;
|
|
23
|
+
/** The bridge's systemd unit. Pure, so the test pins what every host gets. */
|
|
24
|
+
export declare function buzzBridgeUnit(input: {
|
|
25
|
+
agent: string;
|
|
26
|
+
user: string;
|
|
27
|
+
appDir: string;
|
|
28
|
+
}): string;
|
|
29
|
+
export declare function addBuzz(cwd?: string): void;
|
package/dist/buzz.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir, userInfo } from "node:os";
|
|
3
|
+
import { basename, join, resolve } from "node:path";
|
|
4
|
+
import { upsertEnv } from "./envfile.js";
|
|
5
|
+
import { bold, capture, dim, green, red, run, yellow } from "./util.js";
|
|
6
|
+
/**
|
|
7
|
+
* `kyb add buzz` — get an agent ready for its owner's Buzz community.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* Buzz is not a channel file. The agent is a member of the community with its
|
|
11
|
+
* own key, a separate bridge process hears mentions and answers as the person
|
|
12
|
+
* who spoke, and an extension gives the agent the rest of the workspace. Every
|
|
13
|
+
* one of those used to be a manual step on the host, so ticking Buzz on a build
|
|
14
|
+
* did nothing at all.
|
|
15
|
+
*
|
|
16
|
+
* This does everything that does not depend on the customer: the package, the
|
|
17
|
+
* extension mount, the agent's own key, the Buzz CLI and the bridge's unit. The
|
|
18
|
+
* unit is left switched off, because the one thing only the customer can give
|
|
19
|
+
* is which community it lives in. That arrives later through the owner's
|
|
20
|
+
* Channels page (manage's `buzz.join`), which writes the relay, publishes the
|
|
21
|
+
* agent's profile and starts the bridge.
|
|
22
|
+
*/
|
|
23
|
+
export const BUZZ_PACKAGE = "@kybernesis/buzz@^0.10.2";
|
|
24
|
+
export const BUZZ_EXTENSION_MOUNT = `// The agent's hands in Buzz: projects, issues, pull requests, patches, repos,\n` +
|
|
25
|
+
`// long-form notes, channel canvases, workflows, the feed, media — everything the\n` +
|
|
26
|
+
`// workspace has beyond talking, which the bridge alone does not provide.\n` +
|
|
27
|
+
`//\n` +
|
|
28
|
+
`// Actions are signed with THIS AGENT's key and appear under its name.\n` +
|
|
29
|
+
`export { default } from "@kybernesis/buzz/extension";\n`;
|
|
30
|
+
/** Where the agent's Buzz key lives: inside the agent, so the bridge and the agent read the same file. */
|
|
31
|
+
export function buzzKeyPath(appDir) {
|
|
32
|
+
return join(appDir, ".buzz", "agent-key.json");
|
|
33
|
+
}
|
|
34
|
+
export function buzzBridgeUnitName(agent) {
|
|
35
|
+
return `${agent}-buzz-bridge`;
|
|
36
|
+
}
|
|
37
|
+
/** The bridge's systemd unit. Pure, so the test pins what every host gets. */
|
|
38
|
+
export function buzzBridgeUnit(input) {
|
|
39
|
+
const { agent, user, appDir } = input;
|
|
40
|
+
return `# Managed by @kybernesis/create (kyb add buzz). Switched on from the owner's Channels page.
|
|
41
|
+
[Unit]
|
|
42
|
+
Description=${agent} Buzz bridge
|
|
43
|
+
After=network-online.target ${agent}-agent.service
|
|
44
|
+
Wants=network-online.target
|
|
45
|
+
|
|
46
|
+
[Service]
|
|
47
|
+
Type=simple
|
|
48
|
+
User=${user}
|
|
49
|
+
WorkingDirectory=${appDir}
|
|
50
|
+
# The relay, the key and the agent credential all come from .env.local, the
|
|
51
|
+
# same file the agent reads, so they can never disagree.
|
|
52
|
+
ExecStart=/bin/bash -lc 'set -a && . ./.env.local && set +a && exec npx kybernesis-buzz run'
|
|
53
|
+
Restart=always
|
|
54
|
+
RestartSec=10
|
|
55
|
+
# The bridge publishes "offline" on the way out; this gives it time to.
|
|
56
|
+
KillSignal=SIGTERM
|
|
57
|
+
TimeoutStopSec=10
|
|
58
|
+
StartLimitBurst=5
|
|
59
|
+
StartLimitIntervalSec=300
|
|
60
|
+
StandardOutput=append:${appDir}/buzz-bridge.log
|
|
61
|
+
StandardError=append:${appDir}/buzz-bridge.log
|
|
62
|
+
|
|
63
|
+
[Install]
|
|
64
|
+
WantedBy=multi-user.target
|
|
65
|
+
`;
|
|
66
|
+
}
|
|
67
|
+
export function addBuzz(cwd = resolve(process.cwd())) {
|
|
68
|
+
if (!existsSync(join(cwd, "package.json")) || !existsSync(join(cwd, "agent"))) {
|
|
69
|
+
console.error(red("kyb add buzz: run this inside an agent directory."));
|
|
70
|
+
process.exitCode = 1;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (!existsSync(join(cwd, "scripts/eve-server.sh"))) {
|
|
74
|
+
console.error(red("kyb add buzz: Buzz runs a bridge beside the agent, so it needs a host of its own (an exe VM or a server). This agent deploys to Vercel."));
|
|
75
|
+
process.exitCode = 1;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const agent = agentNameOf(cwd);
|
|
79
|
+
console.log(bold("kyb add buzz"));
|
|
80
|
+
console.log(dim(` agent: ${agent}`));
|
|
81
|
+
const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
|
|
82
|
+
if (!pkg.dependencies?.["@kybernesis/buzz"]) {
|
|
83
|
+
run("npm", ["install", BUZZ_PACKAGE, "--no-audit", "--no-fund"], { cwd });
|
|
84
|
+
}
|
|
85
|
+
const mount = join(cwd, "agent/extensions/buzz.ts");
|
|
86
|
+
if (!existsSync(mount)) {
|
|
87
|
+
mkdirSync(join(cwd, "agent/extensions"), { recursive: true });
|
|
88
|
+
writeFileSync(mount, BUZZ_EXTENSION_MOUNT);
|
|
89
|
+
console.log(green(" + agent/extensions/buzz.ts"));
|
|
90
|
+
}
|
|
91
|
+
// One key for the bridge and the agent's tools. An existing BUZZ_KEYFILE is
|
|
92
|
+
// kept: replacing a key is a new member the community never invited.
|
|
93
|
+
const envPath = join(cwd, ".env.local");
|
|
94
|
+
const env = existsSync(envPath) ? readFileSync(envPath, "utf8") : "";
|
|
95
|
+
const existing = env.match(/^BUZZ_KEYFILE\s*=\s*"?([^"\n]+)"?/m)?.[1]?.trim();
|
|
96
|
+
const keyFile = existing || buzzKeyPath(cwd);
|
|
97
|
+
if (!existing)
|
|
98
|
+
upsertEnv(cwd, { BUZZ_KEYFILE: keyFile });
|
|
99
|
+
mkdirSync(join(keyFile, ".."), { recursive: true });
|
|
100
|
+
ignore(cwd, ".buzz/");
|
|
101
|
+
const init = capture("npx", ["kybernesis-buzz", "init"], cwd, { BUZZ_KEYFILE: keyFile });
|
|
102
|
+
const npub = init?.match(/npub1[02-9ac-hj-np-z]+/)?.[0];
|
|
103
|
+
if (!npub) {
|
|
104
|
+
console.error(red(" Could not create the agent's Buzz identity (npx kybernesis-buzz init)."));
|
|
105
|
+
process.exitCode = 1;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
console.log(green(` + Buzz identity ${npub}`));
|
|
109
|
+
// The CLI is what the agent acts through; no binary is published, so it is
|
|
110
|
+
// built once in a container. Without docker the agent can still talk.
|
|
111
|
+
if (!existsSync(join(cwd, ".buzz/bin/buzz"))) {
|
|
112
|
+
const hasDocker = capture("sh", ["-c", "command -v docker >/dev/null && echo yes"])?.trim() === "yes";
|
|
113
|
+
if (hasDocker) {
|
|
114
|
+
console.log(dim(" building the Buzz CLI for this host (first time only, a few minutes) …"));
|
|
115
|
+
const ok = run("npx", ["kybernesis-buzz", "install-cli"], { cwd, allowFail: true, quiet: true, timeoutMs: 15 * 60_000 });
|
|
116
|
+
console.log(ok ? green(" + Buzz CLI (.buzz/bin/buzz)") : yellow(" ! the Buzz CLI did not build; run: npx kybernesis-buzz install-cli"));
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
console.log(yellow(" ! no docker here, so the Buzz CLI was not built; the agent can talk in Buzz but not act in it."));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const unitName = buzzBridgeUnitName(agent);
|
|
123
|
+
const dir = mkdtempSync(join(tmpdir(), "kyb-buzz-"));
|
|
124
|
+
try {
|
|
125
|
+
const file = join(dir, `${unitName}.service`);
|
|
126
|
+
writeFileSync(file, buzzBridgeUnit({ agent, user: userInfo().username, appDir: cwd }));
|
|
127
|
+
const installed = run("sudo", ["-n", "install", "-m", "644", file, `/etc/systemd/system/${unitName}.service`], { allowFail: true, quiet: true }) &&
|
|
128
|
+
run("sudo", ["-n", "systemctl", "daemon-reload"], { allowFail: true, quiet: true });
|
|
129
|
+
console.log(installed
|
|
130
|
+
? green(` + ${unitName}.service (off until the owner connects a community)`)
|
|
131
|
+
: yellow(` ! could not install ${unitName}.service without a password; the owner's Connect step will need it`));
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
rmSync(dir, { recursive: true, force: true });
|
|
135
|
+
}
|
|
136
|
+
console.log(bold("\n Next:"));
|
|
137
|
+
console.log(` · The owner connects it from the Channels page: their community's address, then an admin adds ${npub} as a member.`);
|
|
138
|
+
}
|
|
139
|
+
function agentNameOf(dir) {
|
|
140
|
+
const env = join(dir, ".env.local");
|
|
141
|
+
if (existsSync(env)) {
|
|
142
|
+
const hit = readFileSync(env, "utf8").match(/^KYBERNESIS_AGENT\s*=\s*"?([^"\n]+)"?/m);
|
|
143
|
+
if (hit?.[1])
|
|
144
|
+
return hit[1].trim();
|
|
145
|
+
}
|
|
146
|
+
return basename(dir);
|
|
147
|
+
}
|
|
148
|
+
function ignore(dir, entry) {
|
|
149
|
+
const path = join(dir, ".gitignore");
|
|
150
|
+
const current = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
151
|
+
if (current.split("\n").some((line) => line.trim() === entry))
|
|
152
|
+
return;
|
|
153
|
+
writeFileSync(path, `${current.replace(/\n*$/, "\n")}${entry}\n`);
|
|
154
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -233,6 +233,7 @@ ${dim(" npm i -g @kybernesis/create@latest")}
|
|
|
233
233
|
--yes ${dim("no prompts; take flags and defaults")}
|
|
234
234
|
${bold("kyb add channel <kind>")}
|
|
235
235
|
${dim("slack|imessage|telegram|discord|web — writes the channel, deps and env")}
|
|
236
|
+
${bold("kyb add buzz")} ${dim("the agent's own Buzz identity, tools and bridge (connected from the Channels page)")}
|
|
236
237
|
${bold("kyb add memory")} ${dim("eve 0.49 memory slot backed by Arcana: recall before every turn")}
|
|
237
238
|
${bold("kyb doctor")} preflight checks (keys, issuer, envs, discovery)
|
|
238
239
|
${bold("kyb skills")} install/refresh the FDE skill suite for Claude Code
|
package/dist/doctor.js
CHANGED
|
@@ -61,6 +61,8 @@ function modelReachCheck(reach) {
|
|
|
61
61
|
return { verdict: "pass", label: "Model reach: exe (host LLM integration)" };
|
|
62
62
|
case "grok-sub":
|
|
63
63
|
return { verdict: "pass", label: "Model reach: grok-sub (direct xAI subscription credential)" };
|
|
64
|
+
case "chatgpt-sub":
|
|
65
|
+
return { verdict: "pass", label: "Model reach: chatgpt-sub (ChatGPT subscription through Codex)" };
|
|
64
66
|
case "gateway":
|
|
65
67
|
return { verdict: "pass", label: "Model reach: gateway" };
|
|
66
68
|
case "direct-provider":
|
package/dist/model-reach.d.ts
CHANGED
package/dist/model-reach.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
1
4
|
export const MODEL_REACH_ENV = "KYB_MODEL_REACH";
|
|
2
5
|
export const CLAUDE_SUBSCRIPTION_MODEL = "claude-opus-5";
|
|
3
6
|
/** Resolve the one-shot scaffold selector. An explicit CLI value wins over Factory env. */
|
|
@@ -36,7 +39,7 @@ export function resolveModelScaffold(options) {
|
|
|
36
39
|
export function switchedModelReach(source, env) {
|
|
37
40
|
if (!/\bexeModelReach\s*\(/.test(source))
|
|
38
41
|
return null;
|
|
39
|
-
const authored = /\breach\s*:\s*["'](gateway|byok|claude-sub)["']/.exec(source)?.[1];
|
|
42
|
+
const authored = /\breach\s*:\s*["'](gateway|byok|claude-sub|grok-sub|chatgpt-sub)["']/.exec(source)?.[1];
|
|
40
43
|
if (!authored)
|
|
41
44
|
return { kind: "unresolved", reason: "exeModelReach has no literal default reach to read" };
|
|
42
45
|
const model = env.EXE_MODEL ?? /\bmodel\s*:\s*["']([^"']+)["']/.exec(source)?.[1] ?? "";
|
|
@@ -45,6 +48,18 @@ export function switchedModelReach(source, env) {
|
|
|
45
48
|
return { kind: "exe" };
|
|
46
49
|
if (reach === "claude-sub")
|
|
47
50
|
return { kind: "claude-sub" };
|
|
51
|
+
// Runs only once `grok login` has left a credential; the agent falls back otherwise.
|
|
52
|
+
if (reach === "grok-sub") {
|
|
53
|
+
const auth = env.GROK_AUTH_PATH?.trim() || join(homedir(), ".grok", "auth.json");
|
|
54
|
+
return existsSync(auth) ? { kind: "grok-sub" } : null;
|
|
55
|
+
}
|
|
56
|
+
// Needs eve's chatgpt passed in, and a Codex sign-in on the host.
|
|
57
|
+
if (reach === "chatgpt-sub") {
|
|
58
|
+
if (!/\bchatgpt\s*[,}]/.test(source))
|
|
59
|
+
return null;
|
|
60
|
+
const auth = join(env.CODEX_HOME?.trim() || join(homedir(), ".codex"), "auth.json");
|
|
61
|
+
return existsSync(auth) ? { kind: "chatgpt-sub" } : null;
|
|
62
|
+
}
|
|
48
63
|
if (reach !== "byok")
|
|
49
64
|
return null;
|
|
50
65
|
const provider = /^(anthropic|openai)\//.exec(model)?.[1];
|
package/dist/templates.js
CHANGED
|
@@ -425,9 +425,10 @@ function agentModelTemplate(host, config) {
|
|
|
425
425
|
throw new Error("claude-sub model templates require an exe host");
|
|
426
426
|
if (host === "exe") {
|
|
427
427
|
/**
|
|
428
|
-
* Every exe agent takes the runtime switch: KYB_MODEL_REACH (gateway, byok
|
|
429
|
-
* or
|
|
430
|
-
* by writing env. `
|
|
428
|
+
* Every exe agent takes the runtime switch: KYB_MODEL_REACH (gateway, byok,
|
|
429
|
+
* claude-sub, grok-sub or chatgpt-sub) picks the route at boot, so the
|
|
430
|
+
* setup route can change it by writing env. `chatgpt` is passed so the
|
|
431
|
+
* ChatGPT subscription is one of those routes; nothing calls it until then. `reach` here is what runs while it is unset, which is
|
|
431
432
|
* exactly what this scaffold used to hard-code.
|
|
432
433
|
*
|
|
433
434
|
* The subscription scaffold still names a gateway id, used only if the
|
|
@@ -439,8 +440,9 @@ function agentModelTemplate(host, config) {
|
|
|
439
440
|
return {
|
|
440
441
|
imports: `import { createAnthropic } from "@ai-sdk/anthropic";\n` +
|
|
441
442
|
`import { createOpenAI } from "@ai-sdk/openai";\n` +
|
|
443
|
+
`import { chatgpt } from "eve/models/openai";\n` +
|
|
442
444
|
`import { exeModelReach } from "@kybernesis/exe";\n\n`,
|
|
443
|
-
fields: ` ...exeModelReach({ ${defaults}, createOpenAI, createAnthropic }),\n`,
|
|
445
|
+
fields: ` ...exeModelReach({ ${defaults}, createOpenAI, createAnthropic, chatgpt }),\n`,
|
|
444
446
|
};
|
|
445
447
|
}
|
|
446
448
|
return { imports: "", fields: ` model: ${JSON.stringify(config.model)},\n` };
|
|
@@ -452,7 +454,7 @@ export function hostAgentTs(host, model) {
|
|
|
452
454
|
: (model.reach === "claude-sub"
|
|
453
455
|
? `// By default a Claude subscription through the loopback OAuth proxy — no API key or exe gateway.\n`
|
|
454
456
|
: `// By default the exe.dev LLM integration — no provider key on the host; exe\n// injects the credential (managed gateway, your API key, or a connected\n// ChatGPT subscription) server-side.\n`) +
|
|
455
|
-
`// KYB_MODEL_REACH (gateway | byok | claude-sub) switches the route at boot\n// without editing this file; EXE_MODEL names the gateway or BYOK model.\n`;
|
|
457
|
+
`// KYB_MODEL_REACH (gateway | byok | claude-sub | grok-sub | chatgpt-sub) switches the route at boot\n// without editing this file; EXE_MODEL names the gateway or BYOK model.\n`;
|
|
456
458
|
return `import { defineAgent } from "eve";
|
|
457
459
|
${rendered.imports}${comment}export default defineAgent({
|
|
458
460
|
${rendered.fields}${host === "exe" ? ` // This long-lived self-hosted process has no metered platform session ceiling.\n limits: { maxInputTokensPerSession: false },\n` : ""}});
|
package/dist/upgrade.js
CHANGED
|
@@ -4,6 +4,7 @@ import { tmpdir } from "node:os";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
import { upsertEnv } from "./envfile.js";
|
|
7
|
+
import { BUZZ_EXTENSION_MOUNT } from "./buzz.js";
|
|
7
8
|
import { reconcileHostArtifact } from "./host-artifacts.js";
|
|
8
9
|
import { assessPeerTree, staleRangeWarning } from "./peer-tree.js";
|
|
9
10
|
import { dockerPruneCronArtifact } from "./docker-prune-cron.js";
|
|
@@ -101,18 +102,7 @@ function repairBuzzSetup(cwd, deps) {
|
|
|
101
102
|
const mount = join(cwd, "agent/extensions/buzz.ts");
|
|
102
103
|
if (!existsSync(mount)) {
|
|
103
104
|
mkdirSync(join(cwd, "agent/extensions"), { recursive: true });
|
|
104
|
-
writeFileSync(mount,
|
|
105
|
-
` +
|
|
106
|
-
`// long-form notes, channel canvases, workflows, the feed, media — everything the
|
|
107
|
-
` +
|
|
108
|
-
`// workspace has beyond talking, which the bridge alone does not provide.
|
|
109
|
-
` +
|
|
110
|
-
`//
|
|
111
|
-
` +
|
|
112
|
-
`// Actions are signed with THIS AGENT's key and appear under its name.
|
|
113
|
-
` +
|
|
114
|
-
`export { default } from "@kybernesis/buzz/extension";
|
|
115
|
-
`);
|
|
105
|
+
writeFileSync(mount, BUZZ_EXTENSION_MOUNT);
|
|
116
106
|
done.push("mounted the Buzz extension (agent/extensions/buzz.ts)");
|
|
117
107
|
}
|
|
118
108
|
/**
|
|
@@ -124,7 +114,10 @@ function repairBuzzSetup(cwd, deps) {
|
|
|
124
114
|
*/
|
|
125
115
|
const envPath = join(cwd, ".env.local");
|
|
126
116
|
const env = existsSync(envPath) ? readFileSync(envPath, "utf8") : "";
|
|
127
|
-
|
|
117
|
+
// An agent set up by `kyb add buzz` has no relay until its owner connects a
|
|
118
|
+
// community from the Channels page; that is waiting, not broken.
|
|
119
|
+
const awaitingOwner = /^BUZZ_KEYFILE=/m.test(env) && capture("sh", ["-c", "grep -l 'kyb add buzz' /etc/systemd/system/*buzz-bridge.service 2>/dev/null"]);
|
|
120
|
+
if (!/^BUZZ_RELAY=/m.test(env) && !awaitingOwner) {
|
|
128
121
|
const unit = capture("sh", [
|
|
129
122
|
"-c",
|
|
130
123
|
"grep -ohE 'BUZZ_(RELAY|KEYFILE)=[^ \"]+' /etc/systemd/system/*buzz-bridge.service 2>/dev/null | head -2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kybernesis/create",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "The Kybernesis agent scaffolder and FDE toolkit: one command to a governed, remembering, multiplayer, self-testing eve agent \u2014 plus doctor and upgrade.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -90,8 +90,8 @@ then `eve add @kybernesis/<item>`). Each covers one axis:
|
|
|
90
90
|
`POST .../setup/env`, `POST .../setup/action`): a `manage` grant only;
|
|
91
91
|
allowlisted env (`EXE_MODEL`, `EXE_LLM_URL`, `ANTHROPIC_API_KEY`,
|
|
92
92
|
`OPENAI_API_KEY`, `SLACK_BOT_TOKEN`, `SLACK_SIGNING_SECRET`) and the actions
|
|
93
|
-
`claude.login` (link, then the code on stdin), `linq.signup`/`linq.verify`,
|
|
94
|
-
`link.login` and `vnc.password`. Every change applies at one restart that
|
|
93
|
+
`claude.login` (link, then the code on stdin), `linq.signup`/`linq.verify`, `linq.contacts`/`linq.contact.add`/`linq.contact.remove` (who can text a shared line, 0.15),
|
|
94
|
+
`link.login`/`link.logout`, `wallet.add` (0.16: adds the payments item to an agent with a computer, rebuilds, rolls back on a failed build, restarts only after a good one) and `vnc.password`. Every change applies at one restart that
|
|
95
95
|
waits until no conversation is in flight (deploy.sh's count). A BYOK key only
|
|
96
96
|
takes effect when `agent/agent.ts` uses that provider; status says so.
|
|
97
97
|
Since 0.12 it also takes `KYB_MODEL_REACH` (`gateway`, `byok`,
|