@ouro.bot/cli 0.1.0-alpha.922 → 0.1.0-alpha.924
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/changelog.json +20 -0
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +1 -1
- package/deploy/unraid/sanctuary.xml +1 -1
- package/dist/heart/daemon/cli-exec.js +23 -0
- package/dist/heart/daemon/cli-help.js +7 -0
- package/dist/heart/daemon/cli-parse.js +19 -0
- package/dist/heart/frontend-session-service.js +45 -1
- package/dist/heart/frontend-socket.js +26 -1
- package/dist/heart/streaming.js +3 -3
- package/dist/repertoire/tools-shepherd-guidance.js +166 -0
- package/dist/repertoire/tools-shepherd.js +14 -2
- package/dist/senses/shared-turn.js +11 -0
- package/dist/senses/shepherd/composer.js +317 -0
- package/dist/senses/shepherd/guidance.js +270 -0
- package/dist/senses/shepherd/judge.js +8 -2
- package/dist/senses/shepherd/notice-outbox.js +262 -0
- package/dist/senses/shepherd/owner-notice.js +98 -24
- package/dist/senses/shepherd/sense.js +45 -15
- package/dist/senses/shepherd/surface.js +119 -0
- package/dist/senses/shepherd/target-binding.js +13 -0
- package/dist/senses/shepherd/terminal-process.js +22 -1
- package/dist/senses/teams.js +23 -3
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.SHEPHERD_UUID = void 0;
|
|
37
|
+
exports.resolveExistingLocalOwner = resolveExistingLocalOwner;
|
|
38
|
+
exports.localShepherdFriendStore = localShepherdFriendStore;
|
|
39
|
+
exports.machineShepherdHost = machineShepherdHost;
|
|
40
|
+
exports.listShepherdTargets = listShepherdTargets;
|
|
41
|
+
exports.validateShepherdTarget = validateShepherdTarget;
|
|
42
|
+
exports.authorizedShepherdTargets = authorizedShepherdTargets;
|
|
43
|
+
const path = __importStar(require("node:path"));
|
|
44
|
+
const friends_1 = require("@ouro.bot/friends");
|
|
45
|
+
const identity_1 = require("../../heart/identity");
|
|
46
|
+
const runtime_credentials_1 = require("../../heart/runtime-credentials");
|
|
47
|
+
const shepherd_config_1 = require("../../heart/shepherd-config");
|
|
48
|
+
const tools_shepherd_1 = require("../../repertoire/tools-shepherd");
|
|
49
|
+
const host_1 = require("./host");
|
|
50
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
51
|
+
exports.SHEPHERD_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
52
|
+
/** Resolve the CLI's local identity, but forbid the resolver's first-encounter creation/promotion. */
|
|
53
|
+
async function resolveExistingLocalOwner(store, username = (0, friends_1.machineOwnerUsername)()) {
|
|
54
|
+
if (!username)
|
|
55
|
+
throw new Error("local machine owner identity is unavailable");
|
|
56
|
+
let existing = await store.findByExternalId("local", username);
|
|
57
|
+
if (!existing) {
|
|
58
|
+
const matches = (store.listAll ? await store.listAll() : []).filter(friend => friend.externalIds.some(identity => identity.provider === "local" && identity.externalId.startsWith(`${username}@`)));
|
|
59
|
+
if (matches.length > 1)
|
|
60
|
+
throw new Error("local-family linking is ambiguous; authorized owner setup is required");
|
|
61
|
+
existing = matches[0] ?? null;
|
|
62
|
+
}
|
|
63
|
+
if (!existing || existing.trustLevel !== "family" || existing.admissionState === "revoked") {
|
|
64
|
+
throw new Error(`existing local-family linking is missing for OS user ${username}; authorized owner setup is required`);
|
|
65
|
+
}
|
|
66
|
+
// Snapshot lookup prevents FriendResolver from creating on a transient search failure or
|
|
67
|
+
// migrating an identity as a side effect. The same resolver/channel path still produces context.
|
|
68
|
+
const snapshot = existing;
|
|
69
|
+
const readonlyLookup = Object.create(store);
|
|
70
|
+
readonlyLookup.findByExternalId = async () => snapshot;
|
|
71
|
+
return (await new friends_1.FriendResolver(readonlyLookup, {
|
|
72
|
+
provider: "local", externalId: username, displayName: username, channel: "cli",
|
|
73
|
+
}).resolve()).friend.id;
|
|
74
|
+
}
|
|
75
|
+
function localShepherdFriendStore(agent) {
|
|
76
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(agent))
|
|
77
|
+
throw new Error("Shepherd requires a safe agent name");
|
|
78
|
+
return new friends_1.FileFriendStore(path.join((0, identity_1.getAgentBundlesRoot)(), `${agent}.ouro`, "friends"));
|
|
79
|
+
}
|
|
80
|
+
function machineShepherdHost(agent) {
|
|
81
|
+
const root = path.join((0, identity_1.getAgentBundlesRoot)(), `${agent}.ouro`);
|
|
82
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(agent) || !(0, tools_shepherd_1.shepherdToolsEnabled)(root)) {
|
|
83
|
+
throw new Error("agent does not have Shepherd enabled");
|
|
84
|
+
}
|
|
85
|
+
const machine = (0, runtime_credentials_1.readMachineRuntimeCredentialConfig)(agent);
|
|
86
|
+
if (!machine.ok)
|
|
87
|
+
throw new Error("machine Shepherd connection configuration is unavailable");
|
|
88
|
+
const connection = (0, shepherd_config_1.resolveShepherdConnection)(agent, machine.config);
|
|
89
|
+
if (!connection.ok)
|
|
90
|
+
throw new Error(connection.error);
|
|
91
|
+
if (connection.connection.host !== "cmux")
|
|
92
|
+
throw new Error("the dedicated composer requires cmux stable surface UUIDs");
|
|
93
|
+
return (0, host_1.createShepherdHost)(connection.connection, { agentName: agent });
|
|
94
|
+
}
|
|
95
|
+
async function listShepherdTargets(host, composerTty) {
|
|
96
|
+
return (await host.list()).filter(session => session.agent && exports.SHEPHERD_UUID.test(session.id)
|
|
97
|
+
&& (!composerTty || (session.tty && session.tty.replace(/^\/dev\//, "") !== composerTty.replace(/^\/dev\//, "")))).map(session => Object.freeze({
|
|
98
|
+
id: session.id,
|
|
99
|
+
host: "cmux",
|
|
100
|
+
label: [session.agent, session.cwd, session.workspaceTitle, session.title]
|
|
101
|
+
.filter(Boolean).map(part => part.replace(/[\x00-\x1f\x7f-\x9f]/g, "").slice(0, 160)).join(" / "),
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
async function validateShepherdTarget(host, id) {
|
|
105
|
+
if (!exports.SHEPHERD_UUID.test(id))
|
|
106
|
+
throw new Error("Shepherd session must be a stable UUID");
|
|
107
|
+
const matches = (await listShepherdTargets(host)).filter(target => target.id.toLowerCase() === id.toLowerCase());
|
|
108
|
+
if (matches.length !== 1)
|
|
109
|
+
throw new Error(`selected Shepherd terminal is unavailable: ${id}`);
|
|
110
|
+
return matches[0];
|
|
111
|
+
}
|
|
112
|
+
async function authorizedShepherdTargets(agent, friendId, composerTty) {
|
|
113
|
+
const owner = await resolveExistingLocalOwner(localShepherdFriendStore(agent));
|
|
114
|
+
if (owner !== friendId)
|
|
115
|
+
throw new Error("Shepherd composer requires this OS user's existing local-family identity");
|
|
116
|
+
const targets = await listShepherdTargets(machineShepherdHost(agent), composerTty);
|
|
117
|
+
(0, runtime_1.emitNervesEvent)({ component: "senses", event: "senses.shepherd_targets_resolved", message: "resolved authorized independent composer targets", meta: { agent, count: targets.length } });
|
|
118
|
+
return targets;
|
|
119
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertBoundShepherdTarget = assertBoundShepherdTarget;
|
|
4
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
5
|
+
/** A composer locator is enforced at execution, not merely suggested to the model. */
|
|
6
|
+
function assertBoundShepherdTarget(bound, session, host) {
|
|
7
|
+
if (!bound)
|
|
8
|
+
return;
|
|
9
|
+
if (session !== bound.id || (host !== undefined && host !== bound.host)) {
|
|
10
|
+
(0, runtime_1.emitNervesEvent)({ level: "warn", component: "senses", event: "senses.shepherd_target_refused", message: "refused a terminal operation outside the fixed composer target", meta: { host: bound.host, session: bound.id } });
|
|
11
|
+
throw new Error(`this composer is bound to ${bound.host} UUID ${bound.id}; short refs and other targets are refused`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -43,13 +43,14 @@ async function runCommand(command, args) {
|
|
|
43
43
|
const { stdout } = await exec(command, args, { timeout: 3_000, maxBuffer: 4 * 1024 * 1024 });
|
|
44
44
|
return stdout;
|
|
45
45
|
}
|
|
46
|
-
/**
|
|
46
|
+
/** Batched process/cwd lookups; Node launchers need one additional argv snapshot, never a subprocess per surface. */
|
|
47
47
|
async function identifyTerminalProcesses(ttys, run = runCommand) {
|
|
48
48
|
const result = new Map();
|
|
49
49
|
if (ttys.length === 0)
|
|
50
50
|
return result;
|
|
51
51
|
const wanted = new Set(ttys.map((tty) => tty.replace(/^\/dev\//, "")));
|
|
52
52
|
const agents = new Map();
|
|
53
|
+
const nodeLaunchers = new Map();
|
|
53
54
|
try {
|
|
54
55
|
for (const line of (await run("ps", ["-axo", "pid=,ppid=,pgid=,tpgid=,tty=,comm="])).split("\n")) {
|
|
55
56
|
const match = line.trim().match(/^(\d+)\s+(\d+)\s+(\d+)\s+(-?\d+)\s+(\S+)\s+(.+)$/);
|
|
@@ -59,6 +60,8 @@ async function identifyTerminalProcesses(ttys, run = runCommand) {
|
|
|
59
60
|
if (!wanted.has(tty) || group !== foreground)
|
|
60
61
|
continue;
|
|
61
62
|
const executable = path.basename(command);
|
|
63
|
+
if (executable === "node")
|
|
64
|
+
nodeLaunchers.set(Number(pid), { parent: Number(parent), tty: tty });
|
|
62
65
|
const agent = executable === "claude" ? "Claude Code" : executable === "copilot" ? "Copilot" : executable === "codex" ? "Codex" : null;
|
|
63
66
|
if (!agent)
|
|
64
67
|
continue;
|
|
@@ -71,6 +74,24 @@ async function identifyTerminalProcesses(ttys, run = runCommand) {
|
|
|
71
74
|
(0, runtime_1.emitNervesEvent)({ level: "warn", component: "senses", event: "senses.shepherd_process_lookup_error", message: "could not identify terminal foreground agents", meta: { error: String(error) } });
|
|
72
75
|
return result;
|
|
73
76
|
}
|
|
77
|
+
if (nodeLaunchers.size > 0) {
|
|
78
|
+
try {
|
|
79
|
+
for (const line of (await run("ps", ["-p", [...nodeLaunchers.keys()].join(","), "-o", "pid=,args="])).split("\n")) {
|
|
80
|
+
const match = line.trim().match(/^(\d+)\s+\S+\s+(\S+)/);
|
|
81
|
+
if (!match)
|
|
82
|
+
continue;
|
|
83
|
+
const launcher = nodeLaunchers.get(Number(match[1]));
|
|
84
|
+
if (!launcher || !/(?:^|\/)copilot\/pkg\/[^/]+\/[^/]+\/cli-main\.js$/.test(match[2]))
|
|
85
|
+
continue;
|
|
86
|
+
const candidates = agents.get(launcher.tty) ?? [];
|
|
87
|
+
candidates.push({ pid: Number(match[1]), parent: launcher.parent, agent: "Copilot" });
|
|
88
|
+
agents.set(launcher.tty, candidates);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
(0, runtime_1.emitNervesEvent)({ level: "warn", component: "senses", event: "senses.shepherd_process_lookup_error", message: "could not identify Node-hosted terminal agents", meta: { error: String(error) } });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
74
95
|
const selected = new Map();
|
|
75
96
|
for (const [tty, candidates] of agents) {
|
|
76
97
|
const outer = candidates.find((candidate) => !candidates.some((parent) => parent.pid === candidate.parent));
|
package/dist/senses/teams.js
CHANGED
|
@@ -1278,6 +1278,8 @@ async function sendProactiveTeamsMessageToSession(params, deps) {
|
|
|
1278
1278
|
});
|
|
1279
1279
|
return { delivered: false, reason: "internal_content_blocked" };
|
|
1280
1280
|
}
|
|
1281
|
+
let conversationId;
|
|
1282
|
+
let activityAttempted = false;
|
|
1281
1283
|
try {
|
|
1282
1284
|
const conversation = await conversations.create({
|
|
1283
1285
|
bot: { id: deps.botApi.id },
|
|
@@ -1285,7 +1287,9 @@ async function sendProactiveTeamsMessageToSession(params, deps) {
|
|
|
1285
1287
|
tenantId: aadInfo.tenantId,
|
|
1286
1288
|
isGroup: false,
|
|
1287
1289
|
});
|
|
1288
|
-
|
|
1290
|
+
conversationId = conversation.id;
|
|
1291
|
+
activityAttempted = true;
|
|
1292
|
+
const activity = await conversations.activities(conversation.id).create({
|
|
1289
1293
|
type: "message",
|
|
1290
1294
|
text: params.text,
|
|
1291
1295
|
...(params.textFormat ? { textFormat: params.textFormat } : {}),
|
|
@@ -1296,7 +1300,11 @@ async function sendProactiveTeamsMessageToSession(params, deps) {
|
|
|
1296
1300
|
message: "proactive teams message sent",
|
|
1297
1301
|
meta: { friendId: params.friendId, aadObjectId: aadInfo.aadObjectId, sessionKey: params.sessionKey },
|
|
1298
1302
|
});
|
|
1299
|
-
|
|
1303
|
+
const messageId = activity && typeof activity === "object" && "id" in activity && typeof activity.id === "string" ? activity.id : undefined;
|
|
1304
|
+
return {
|
|
1305
|
+
delivered: true,
|
|
1306
|
+
...(params.captureReceipt ? { delivery: { state: "accepted", conversationId, ...(messageId ? { messageId } : {}) } } : {}),
|
|
1307
|
+
};
|
|
1300
1308
|
}
|
|
1301
1309
|
catch (error) {
|
|
1302
1310
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -1311,7 +1319,19 @@ async function sendProactiveTeamsMessageToSession(params, deps) {
|
|
|
1311
1319
|
reason: error instanceof Error ? error.message : String(error),
|
|
1312
1320
|
},
|
|
1313
1321
|
});
|
|
1314
|
-
|
|
1322
|
+
let status;
|
|
1323
|
+
if (error && typeof error === "object") {
|
|
1324
|
+
if ("statusCode" in error)
|
|
1325
|
+
status = error.statusCode;
|
|
1326
|
+
else if ("status" in error)
|
|
1327
|
+
status = error.status;
|
|
1328
|
+
}
|
|
1329
|
+
const rejected = typeof status === "number" && status >= 400 && status < 500 && status !== 408 && status !== 409;
|
|
1330
|
+
const state = !activityAttempted || rejected ? "not_sent" : "unknown";
|
|
1331
|
+
return {
|
|
1332
|
+
delivered: false, reason: "send_error",
|
|
1333
|
+
...(params.captureReceipt ? { delivery: { state, ...(conversationId ? { conversationId } : {}) } } : {}),
|
|
1334
|
+
};
|
|
1315
1335
|
}
|
|
1316
1336
|
}
|
|
1317
1337
|
function scanPendingTeamsFiles(pendingRoot) {
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ouro.bot/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.924",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@ouro.bot/cli",
|
|
9
|
-
"version": "0.1.0-alpha.
|
|
9
|
+
"version": "0.1.0-alpha.924",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
12
12
|
"@azure/identity": "^4.13.0",
|