@lambdacurry/arbor 0.4.34 → 0.6.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/README.md +1 -1
- package/dist/arbor.js +80 -18
- package/package.json +18 -18
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ arbor whoami
|
|
|
32
32
|
|
|
33
33
|
## Agent self-registration (AD-109)
|
|
34
34
|
|
|
35
|
-
An agent can register
|
|
35
|
+
An agent can register _itself_ with a device-code-style handshake. A human mints a one-time **pairing key** in the Arbor web app (Settings → "Have an agent register itself") and hands it to the agent. The agent then:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
38
|
npx @lambdacurry/arbor connect <pairing-key>
|
package/dist/arbor.js
CHANGED
|
@@ -1446,8 +1446,16 @@ var initiatives = sqliteTable("initiatives", {
|
|
|
1446
1446
|
ownerProfileId: text("owner_profile_id").notNull().references(() => profiles.id),
|
|
1447
1447
|
priority: text("priority").$type().notNull().default("normal"),
|
|
1448
1448
|
status: text("status").$type().notNull().default("active"),
|
|
1449
|
+
homeSpaceId: text("home_space_id").references(() => spaces.id),
|
|
1449
1450
|
createdAt: ts("created_at").notNull()
|
|
1450
1451
|
}, (t) => ({ orgIdx: index("initiatives_org_idx").on(t.orgId) }));
|
|
1452
|
+
var initiativeSpaces = sqliteTable("initiative_spaces", {
|
|
1453
|
+
initiativeId: text("initiative_id").notNull().references(() => initiatives.id),
|
|
1454
|
+
spaceId: text("space_id").notNull().references(() => spaces.id)
|
|
1455
|
+
}, (t) => ({
|
|
1456
|
+
pk: primaryKey({ columns: [t.initiativeId, t.spaceId] }),
|
|
1457
|
+
spaceIdx: index("initiative_spaces_space_idx").on(t.spaceId)
|
|
1458
|
+
}));
|
|
1451
1459
|
var threads = sqliteTable("threads", {
|
|
1452
1460
|
id: text("id").primaryKey(),
|
|
1453
1461
|
topicId: text("topic_id").notNull().references(() => topics.id),
|
|
@@ -16416,15 +16424,15 @@ var ACTIONS = [
|
|
|
16416
16424
|
run: forward("space.leave")
|
|
16417
16425
|
},
|
|
16418
16426
|
{
|
|
16419
|
-
name: "
|
|
16420
|
-
title: "
|
|
16421
|
-
description:
|
|
16427
|
+
name: "add_to_space",
|
|
16428
|
+
title: "Add a member to a space",
|
|
16429
|
+
description: 'Add a person or agent to a space you administer (space-admin or org-owner) — the way into a private space. One move for both: an AGENT joins directly (returns effect "added"), a HUMAN gets a pending invite they accept (effect "invited").',
|
|
16422
16430
|
inputSchema: {
|
|
16423
|
-
spaceId: exports_external.string().describe("the space id to
|
|
16424
|
-
profileId: exports_external.string().describe("the
|
|
16431
|
+
spaceId: exports_external.string().describe("the space id to add into, e.g. spc_…"),
|
|
16432
|
+
profileId: exports_external.string().describe("the profile id to add, e.g. prof_… (agent → granted, human → invited)")
|
|
16425
16433
|
},
|
|
16426
16434
|
surfaces: ["mcp", "cli"],
|
|
16427
|
-
run: forward("space.
|
|
16435
|
+
run: forward("space.addMember")
|
|
16428
16436
|
},
|
|
16429
16437
|
{
|
|
16430
16438
|
name: "accept_space_invite",
|
|
@@ -16455,9 +16463,9 @@ var ACTIONS = [
|
|
|
16455
16463
|
{
|
|
16456
16464
|
name: "create_goal",
|
|
16457
16465
|
title: "Create a goal",
|
|
16458
|
-
description: "Create
|
|
16466
|
+
description: "Create a GOAL (initiative) — the widest unit of work, the 'why' that threads serve toward. The space you create it from becomes its HOME (AD-180): the goal surfaces there immediately, and that space's threads can bind to it. To surface it in MORE spaces, `attach_goal_to_space`. Reach for this when several threads ladder up to one outcome worth naming; prefer linking to an existing goal (see `list_goals`) over minting a near-duplicate.",
|
|
16459
16467
|
inputSchema: {
|
|
16460
|
-
spaceId: exports_external.string().describe("the space
|
|
16468
|
+
spaceId: exports_external.string().describe("the space it's created in — becomes its HOME space, spc_…"),
|
|
16461
16469
|
title: exports_external.string().min(1).describe("the goal's name — an outcome, not a task"),
|
|
16462
16470
|
purpose: exports_external.string().optional().describe("one or two lines on what reaching it means"),
|
|
16463
16471
|
threadIds: exports_external.array(exports_external.string()).optional().describe("optional threads IN THIS SPACE to link at creation (others are ignored)")
|
|
@@ -16465,6 +16473,28 @@ var ACTIONS = [
|
|
|
16465
16473
|
surfaces: ["mcp", "cli"],
|
|
16466
16474
|
run: forward("initiative.create")
|
|
16467
16475
|
},
|
|
16476
|
+
{
|
|
16477
|
+
name: "attach_goal_to_space",
|
|
16478
|
+
title: "Attach a goal to a space",
|
|
16479
|
+
description: "Surface a goal in an ADDITIONAL space (AD-180) — visibility, not custody: it appears on that space's goal strip and its threads can then `link_thread_to_goal` to it. You must be a member of the target space. Use for a genuinely cross-space goal (a launch spanning product + marketing); a goal already surfaces in its home space without this. Idempotent.",
|
|
16480
|
+
inputSchema: {
|
|
16481
|
+
initiativeId: exports_external.string().describe("the goal to attach, ini_…"),
|
|
16482
|
+
spaceId: exports_external.string().describe("the space to surface it in, spc_… (you must be a member)")
|
|
16483
|
+
},
|
|
16484
|
+
surfaces: ["mcp", "cli"],
|
|
16485
|
+
run: forward("initiative.attachSpace")
|
|
16486
|
+
},
|
|
16487
|
+
{
|
|
16488
|
+
name: "detach_goal_from_space",
|
|
16489
|
+
title: "Detach a goal from a space",
|
|
16490
|
+
description: "Stop surfacing a goal in a space (the deliberate inverse of `attach_goal_to_space`). A goal can NOT be detached from its HOME space — archive it instead. Existing thread links are untouched; only where the goal surfaces changes.",
|
|
16491
|
+
inputSchema: {
|
|
16492
|
+
initiativeId: exports_external.string().describe("the goal to detach, ini_…"),
|
|
16493
|
+
spaceId: exports_external.string().describe("the space to stop surfacing it in, spc_…")
|
|
16494
|
+
},
|
|
16495
|
+
surfaces: ["mcp", "cli"],
|
|
16496
|
+
run: forward("initiative.detachSpace")
|
|
16497
|
+
},
|
|
16468
16498
|
{
|
|
16469
16499
|
name: "link_thread_to_goal",
|
|
16470
16500
|
title: "Link a thread to a goal",
|
|
@@ -16490,7 +16520,7 @@ var ACTIONS = [
|
|
|
16490
16520
|
{
|
|
16491
16521
|
name: "list_goals",
|
|
16492
16522
|
title: "List a thread's goals",
|
|
16493
|
-
description: "For a thread, list the goals it already contributes to PLUS the
|
|
16523
|
+
description: "For a thread, list the goals it already contributes to PLUS the pick list of goals attached to ITS space (AD-180 — a thread binds only to goals that surface where it lives; `attach_goal_to_space` widens a goal's reach). Reach for this before `link_thread_to_goal` to find the right goal id and avoid creating a duplicate; archived goals are omitted.",
|
|
16494
16524
|
inputSchema: {
|
|
16495
16525
|
threadId: exports_external.string().describe("the thread to read goals for, thr_…")
|
|
16496
16526
|
},
|
|
@@ -16533,11 +16563,12 @@ var ACTIONS = [
|
|
|
16533
16563
|
{
|
|
16534
16564
|
name: "space_create",
|
|
16535
16565
|
title: "Create a space",
|
|
16536
|
-
description: "Create a top-level space — the org-foundation container topics and threads live under. ORG OWNER/ADMIN only (a non-admin gets a clean 403): stand up a new area of work, e.g. a 'Saffron' space for cross-agent infra/tooling deliberation. Pass visibility private for invite-only (default open).",
|
|
16566
|
+
description: "Create a top-level space — the org-foundation container topics and threads live under. ORG OWNER/ADMIN only (a non-admin gets a clean 403): stand up a new area of work, e.g. a 'Saffron' space for cross-agent infra/tooling deliberation. Pass visibility private for invite-only (default open). Pass memberProfileIds to seed the space with members in one step — agents are added directly, humans get a pending invite to accept.",
|
|
16537
16567
|
inputSchema: {
|
|
16538
16568
|
title: exports_external.string().min(1).describe("the space title"),
|
|
16539
16569
|
purpose: exports_external.string().optional().describe("optional one-line purpose"),
|
|
16540
|
-
visibility: exports_external.enum(["open", "private"]).optional().describe("open (default) or private (invite-only)")
|
|
16570
|
+
visibility: exports_external.enum(["open", "private"]).optional().describe("open (default) or private (invite-only)"),
|
|
16571
|
+
memberProfileIds: exports_external.array(exports_external.string()).optional().describe("org profile ids to seed as members — agents added directly, humans get a pending invite")
|
|
16541
16572
|
},
|
|
16542
16573
|
surfaces: ["mcp", "cli"],
|
|
16543
16574
|
run: forward("space.create")
|
|
@@ -16775,8 +16806,18 @@ import { randomUUID } from "node:crypto";
|
|
|
16775
16806
|
function lastValue(value) {
|
|
16776
16807
|
return Array.isArray(value) ? value[value.length - 1] : value;
|
|
16777
16808
|
}
|
|
16778
|
-
var GLOBAL_BOOLEAN_FLAGS = new Set([
|
|
16779
|
-
|
|
16809
|
+
var GLOBAL_BOOLEAN_FLAGS = new Set([
|
|
16810
|
+
"json",
|
|
16811
|
+
"quiet",
|
|
16812
|
+
"no-quiet",
|
|
16813
|
+
"version",
|
|
16814
|
+
"help"
|
|
16815
|
+
]);
|
|
16816
|
+
var RESERVED_FLAGS = new Set([
|
|
16817
|
+
...GLOBAL_BOOLEAN_FLAGS,
|
|
16818
|
+
"url",
|
|
16819
|
+
"fields"
|
|
16820
|
+
]);
|
|
16780
16821
|
var TRUTHY = new Set(["true", "1", "yes", "on"]);
|
|
16781
16822
|
function truthyFlag(value) {
|
|
16782
16823
|
const v = lastValue(value);
|
|
@@ -16823,7 +16864,11 @@ function applyFields(data, fields) {
|
|
|
16823
16864
|
function emitData(data, action, ctx) {
|
|
16824
16865
|
const shaped = ctx.fields ? applyFields(data, ctx.fields) : data;
|
|
16825
16866
|
if (ctx.json) {
|
|
16826
|
-
const envelope = {
|
|
16867
|
+
const envelope = {
|
|
16868
|
+
ok: true,
|
|
16869
|
+
data: shaped,
|
|
16870
|
+
meta: { action, requestId: randomUUID() }
|
|
16871
|
+
};
|
|
16827
16872
|
process.stdout.write(`${JSON.stringify(envelope)}
|
|
16828
16873
|
`);
|
|
16829
16874
|
} else {
|
|
@@ -16835,7 +16880,11 @@ function emitError(err, action, ctx) {
|
|
|
16835
16880
|
const code = codeForError(err);
|
|
16836
16881
|
const message = err instanceof Error ? err.message : String(err);
|
|
16837
16882
|
if (ctx.json) {
|
|
16838
|
-
const envelope = {
|
|
16883
|
+
const envelope = {
|
|
16884
|
+
ok: false,
|
|
16885
|
+
error: { code, message },
|
|
16886
|
+
meta: { action, requestId: randomUUID() }
|
|
16887
|
+
};
|
|
16839
16888
|
process.stdout.write(`${JSON.stringify(envelope)}
|
|
16840
16889
|
`);
|
|
16841
16890
|
process.exitCode = exitCodeForError(code);
|
|
@@ -17138,7 +17187,11 @@ async function connect(opts) {
|
|
|
17138
17187
|
throw new Error("pairing timed out — ask for a new pairing key and run `arbor connect` again");
|
|
17139
17188
|
}
|
|
17140
17189
|
await sleep(pollIntervalMs);
|
|
17141
|
-
const tokRes = await fetch(`${apiUrl}/api/agent/connect/token`, {
|
|
17190
|
+
const tokRes = await fetch(`${apiUrl}/api/agent/connect/token`, {
|
|
17191
|
+
method: "POST",
|
|
17192
|
+
headers,
|
|
17193
|
+
body
|
|
17194
|
+
});
|
|
17142
17195
|
const tok = await tokRes.json().catch(() => ({}));
|
|
17143
17196
|
if (tok.status === "approved" && tok.apiKey) {
|
|
17144
17197
|
saveConfig({ apiUrl, token: tok.apiKey });
|
|
@@ -17188,7 +17241,11 @@ async function login(opts) {
|
|
|
17188
17241
|
const tokRes = await fetch(`${apiUrl}/api/auth/device/token`, {
|
|
17189
17242
|
method: "POST",
|
|
17190
17243
|
headers: { "content-type": "application/json" },
|
|
17191
|
-
body: JSON.stringify({
|
|
17244
|
+
body: JSON.stringify({
|
|
17245
|
+
grant_type: GRANT_TYPE,
|
|
17246
|
+
device_code: code.device_code,
|
|
17247
|
+
client_id: CLIENT_ID
|
|
17248
|
+
})
|
|
17192
17249
|
});
|
|
17193
17250
|
const tok = await tokRes.json().catch(() => ({}));
|
|
17194
17251
|
if (tok.access_token) {
|
|
@@ -17333,7 +17390,12 @@ async function renderHealth(ctx) {
|
|
|
17333
17390
|
const me = await httpExecutor.call("me.get", {});
|
|
17334
17391
|
auth = {
|
|
17335
17392
|
authenticated: true,
|
|
17336
|
-
profile: {
|
|
17393
|
+
profile: {
|
|
17394
|
+
id: me.profile.id,
|
|
17395
|
+
displayName: me.profile.displayName,
|
|
17396
|
+
kind: me.profile.kind,
|
|
17397
|
+
orgId: me.profile.orgId
|
|
17398
|
+
}
|
|
17337
17399
|
};
|
|
17338
17400
|
} catch (e) {
|
|
17339
17401
|
auth = { authenticated: false, error: e instanceof Error ? e.message : String(e) };
|
package/package.json
CHANGED
|
@@ -1,27 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lambdacurry/arbor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "The Arbor CLI — a shared workspace for people and agents. The human + headless-agent write path over Arbor's guarded operation surface.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"arbor": "./dist/arbor.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"dist",
|
|
11
|
-
"README.md"
|
|
12
|
-
],
|
|
13
|
-
"publishConfig": {
|
|
14
|
-
"access": "public"
|
|
15
|
-
},
|
|
16
|
-
"engines": {
|
|
17
|
-
"node": ">=18"
|
|
18
|
-
},
|
|
19
5
|
"keywords": [
|
|
6
|
+
"agents",
|
|
20
7
|
"arbor",
|
|
21
8
|
"cli",
|
|
22
|
-
"
|
|
23
|
-
"mcp"
|
|
24
|
-
"collaboration"
|
|
9
|
+
"collaboration",
|
|
10
|
+
"mcp"
|
|
25
11
|
],
|
|
26
12
|
"license": "MIT",
|
|
27
13
|
"repository": {
|
|
@@ -29,6 +15,17 @@
|
|
|
29
15
|
"url": "git+https://github.com/lambda-curry/arbor.git",
|
|
30
16
|
"directory": "packages/cli"
|
|
31
17
|
},
|
|
18
|
+
"bin": {
|
|
19
|
+
"arbor": "./dist/arbor.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
32
29
|
"scripts": {
|
|
33
30
|
"arbor": "tsx src/bin.ts",
|
|
34
31
|
"build": "bun build ./src/bin.ts --target=node --outfile=dist/arbor.js",
|
|
@@ -44,5 +41,8 @@
|
|
|
44
41
|
"typescript": "^5.8.0",
|
|
45
42
|
"vitest": "^4.1.4",
|
|
46
43
|
"zod": "^4.4.3"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18"
|
|
47
47
|
}
|
|
48
48
|
}
|