@ours.network/fleet 1.1.0-nightly.2 → 1.1.0-nightly.21
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 +204 -55
- package/dist/agent-recovery-gate.d.ts +17 -0
- package/dist/agent-recovery-gate.js +135 -0
- package/dist/application/capabilities.js +18 -6
- package/dist/application/fleet-query-service.js +20 -5
- package/dist/application/role-command-service.js +9 -0
- package/dist/application/role-creation-service.d.ts +6 -25
- package/dist/application/role-creation-service.js +34 -72
- package/dist/application/role-repository.d.ts +4 -2
- package/dist/application/role-repository.js +6 -3
- package/dist/application/session-control.d.ts +3 -1
- package/dist/application/session-control.js +4 -2
- package/dist/application/task-room-service.d.ts +58 -6
- package/dist/application/task-room-service.js +435 -79
- package/dist/briefing.js +74 -24
- package/dist/build-info.json +4 -4
- package/dist/canonical-json.d.ts +2 -0
- package/dist/canonical-json.js +10 -0
- package/dist/cli.js +241 -76
- package/dist/config-yaml.d.ts +1 -1
- package/dist/config.d.ts +75 -3
- package/dist/config.js +274 -34
- package/dist/creation.d.ts +1 -1
- package/dist/creation.js +3 -2
- package/dist/daemon-recovery.d.ts +93 -0
- package/dist/daemon-recovery.js +328 -0
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +192 -66
- package/dist/doctor.d.ts +2 -0
- package/dist/doctor.js +54 -4
- package/dist/fleet-command-audit.d.ts +165 -0
- package/dist/fleet-command-audit.js +555 -0
- package/dist/fleet-proxy.d.ts +7 -2
- package/dist/fleet-proxy.js +30 -8
- package/dist/harness/agent-session.d.ts +5 -0
- package/dist/harness/claude-code-session.d.ts +2 -0
- package/dist/harness/claude-code-session.js +6 -1
- package/dist/harness/claude-code.js +2 -0
- package/dist/harness/codex-app-server-proxy.d.ts +40 -0
- package/dist/harness/codex-app-server-proxy.js +348 -14
- package/dist/harness/codex-session.d.ts +11 -2
- package/dist/harness/codex-session.js +22 -2
- package/dist/harness/codex.d.ts +5 -1
- package/dist/harness/codex.js +110 -10
- package/dist/harness/types.d.ts +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +4 -3
- package/dist/init-guidance.d.ts +1 -1
- package/dist/init-guidance.js +4 -3
- package/dist/init-wizard.d.ts +98 -0
- package/dist/init-wizard.js +629 -0
- package/dist/lifecycle-summary.d.ts +134 -0
- package/dist/lifecycle-summary.js +179 -0
- package/dist/loops/config.d.ts +10 -1
- package/dist/loops/config.js +38 -5
- package/dist/model-env.d.ts +2 -2
- package/dist/model-env.js +1 -1
- package/dist/model-recovery.d.ts +1 -1
- package/dist/ops.js +2 -17
- package/dist/owner-channel/channel.d.ts +58 -0
- package/dist/owner-channel/channel.js +321 -42
- package/dist/owner-channel/commands.d.ts +6 -1
- package/dist/owner-channel/commands.js +34 -19
- package/dist/owner-channel/state.d.ts +2 -0
- package/dist/owner-channel/state.js +8 -2
- package/dist/permissions.js +1 -1
- package/dist/preset-bootstrap.d.ts +10 -0
- package/dist/preset-bootstrap.js +85 -0
- package/dist/preset-migration.d.ts +42 -0
- package/dist/preset-migration.js +414 -0
- package/dist/reports/artifact.d.ts +10 -0
- package/dist/reports/artifact.js +32 -0
- package/dist/reports/index.d.ts +5 -0
- package/dist/reports/index.js +5 -0
- package/dist/reports/render.d.ts +6 -0
- package/dist/reports/render.js +240 -0
- package/dist/reports/service.d.ts +42 -0
- package/dist/reports/service.js +57 -0
- package/dist/reports/tasks.d.ts +52 -0
- package/dist/reports/tasks.js +177 -0
- package/dist/reports/types.d.ts +315 -0
- package/dist/reports/types.js +6 -0
- package/dist/resolved-plan.js +13 -1
- package/dist/rooms-tasks/cli.d.ts +2 -0
- package/dist/rooms-tasks/cli.js +514 -75
- package/dist/rooms-tasks/close.d.ts +9 -1
- package/dist/rooms-tasks/close.js +20 -4
- package/dist/rooms-tasks/config.js +16 -14
- package/dist/rooms-tasks/cowork-adapter.d.ts +1 -0
- package/dist/rooms-tasks/cowork-adapter.js +6 -0
- package/dist/rooms-tasks/deletion.d.ts +51 -0
- package/dist/rooms-tasks/deletion.js +217 -0
- package/dist/rooms-tasks/external-worker.d.ts +1 -0
- package/dist/rooms-tasks/external-worker.js +14 -9
- package/dist/rooms-tasks/launch-snapshot.d.ts +19 -0
- package/dist/rooms-tasks/launch-snapshot.js +181 -0
- package/dist/rooms-tasks/member-overrides.d.ts +30 -0
- package/dist/rooms-tasks/member-overrides.js +204 -0
- package/dist/rooms-tasks/provision.js +184 -49
- package/dist/rooms-tasks/room-state.d.ts +1 -0
- package/dist/rooms-tasks/room-state.js +12 -1
- package/dist/rooms-tasks/task-state.d.ts +115 -3
- package/dist/rooms-tasks/task-state.js +361 -11
- package/dist/rooms-tasks/templates.d.ts +4 -2
- package/dist/rooms-tasks/templates.js +42 -78
- package/dist/rooms-tasks/terminal.d.ts +3 -0
- package/dist/rooms-tasks/terminal.js +7 -3
- package/dist/rooms-tasks/types.d.ts +96 -17
- package/dist/rooms-tasks/types.js +14 -4
- package/dist/runner.d.ts +12 -2
- package/dist/runner.js +124 -43
- package/dist/session/acp.d.ts +9 -1
- package/dist/session/acp.js +35 -9
- package/dist/session/arbiter.d.ts +2 -1
- package/dist/session/arbiter.js +2 -0
- package/dist/session/codex-app-server-transport.d.ts +53 -0
- package/dist/session/codex-app-server-transport.js +184 -0
- package/dist/session/codex-app-server.d.ts +122 -0
- package/dist/session/codex-app-server.js +1031 -0
- package/dist/session/control.d.ts +14 -1
- package/dist/session/control.js +38 -0
- package/dist/session/conversation-types.d.ts +4 -4
- package/dist/session/types.d.ts +24 -6
- package/dist/session/types.js +28 -0
- package/dist/spawn.d.ts +15 -31
- package/dist/spawn.js +117 -237
- package/dist/temp-lifecycle.d.ts +1 -1
- package/dist/watchdog/config.d.ts +10 -7
- package/dist/watchdog/config.js +29 -19
- package/dist/watchdog/run.js +4 -13
- package/dist/web/fleet-config-service.d.ts +1 -0
- package/dist/web/fleet-config-service.js +27 -8
- package/dist/web/runtime.js +11 -6
- package/dist/web/server.js +28 -1
- package/dist/web/topology-promote.js +8 -1
- package/dist/web-app/assets/index-C0h3ALvs.js +10 -0
- package/dist/web-app/index.html +1 -1
- package/package.json +3 -2
- package/presets/brain-catalog.json +16 -0
- package/presets/fleet/agent_templates/Critic.yaml +4 -0
- package/presets/fleet/agent_templates/Developer.yaml +4 -0
- package/presets/fleet/agent_templates/LocalCoordinator.yaml +4 -0
- package/presets/fleet/agents/FleetCoordinator.yaml +3 -0
- package/presets/fleet/brains/claude-default.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-high.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-low.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-max.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-medium.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-high.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-low.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-max.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-medium.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-sonnet-5-high.yaml +5 -0
- package/presets/fleet/brains/claude-sonnet-5-low.yaml +5 -0
- package/presets/fleet/brains/claude-sonnet-5-max.yaml +5 -0
- package/presets/fleet/brains/claude-sonnet-5-medium.yaml +5 -0
- package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-5-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-5-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +5 -0
- package/presets/fleet/roles/Coordinator.yaml +30 -0
- package/presets/fleet/roles/Critic.yaml +19 -0
- package/presets/fleet/roles/Developer.yaml +19 -0
- package/presets/fleet/roles/LocalCoordinator.yaml +25 -0
- package/presets/fleet/room_templates/pair.yaml +10 -0
- package/presets/fleet/room_templates/single.yaml +7 -0
- package/presets/fleet/room_templates/team.yaml +12 -0
- package/presets/fleet.yaml +7 -0
- package/presets/manifest.json +6 -0
- package/dist/web-app/assets/index-BbE9EX6n.js +0 -10
|
@@ -6,6 +6,16 @@ import { markdownCode, markdownProse, renderMarkdownFailure, renderMarkdownList,
|
|
|
6
6
|
import { OWNER_COMMENT_LABEL, ownerNotices } from './notices.js';
|
|
7
7
|
import { TaskRoomApplicationError } from '../application/task-room-service.js';
|
|
8
8
|
import { TaskListError } from '../rooms-tasks/task-lists.js';
|
|
9
|
+
import { createTasksReport } from '../reports/index.js';
|
|
10
|
+
async function ownerTaskReport(ctx) {
|
|
11
|
+
return createTasksReport({
|
|
12
|
+
viewer: { surface: 'messenger', authenticatedCid: ctx.authenticatedCid ?? 'owner-command-context', roomCids: [] },
|
|
13
|
+
collect: () => {
|
|
14
|
+
return { lists: ctx.listTaskLists(), tasks: ctx.listTasks() };
|
|
15
|
+
},
|
|
16
|
+
source: { name: 'ours-fleet', version: ctx.version },
|
|
17
|
+
});
|
|
18
|
+
}
|
|
9
19
|
/** A malformed invocation; the dispatcher answers it with annotated help. */
|
|
10
20
|
class OwnerCommandUsageError extends Error {
|
|
11
21
|
}
|
|
@@ -26,7 +36,7 @@ function isKnownOwnerTaskState(message) {
|
|
|
26
36
|
/^task is not blocked$/u,
|
|
27
37
|
new RegExp(`^task ${SAFE_ID_WORD} already has a conflicting '(?:done|cancelled)' terminal intent$`, 'u'),
|
|
28
38
|
new RegExp(`^task ${SAFE_ID_WORD} is already in terminal state '${TASK_STATE_WORD}'$`, 'u'),
|
|
29
|
-
new RegExp(`^
|
|
39
|
+
new RegExp(`^task ${SAFE_ID_WORD} is pending deletion; run 'ours-fleet task delete ${SAFE_ID_WORD} ${SAFE_ID_WORD}' to retry cleanup$`, 'u'),
|
|
30
40
|
].some(pattern => pattern.test(message));
|
|
31
41
|
}
|
|
32
42
|
function isKnownOwnerRoomState(message) {
|
|
@@ -242,10 +252,18 @@ export const ownerCommands = [
|
|
|
242
252
|
execute: noArgs('/version', async (ctx) => ctx.reply(`ℹ️ ours-fleet ${ctx.version}`)),
|
|
243
253
|
},
|
|
244
254
|
{
|
|
245
|
-
name: 'tasks', usage: '/tasks [state]',
|
|
246
|
-
summary: 'list tasks
|
|
255
|
+
name: 'tasks', usage: '/tasks [state|html]',
|
|
256
|
+
summary: 'list tasks, or attach the HTML report with /tasks html',
|
|
247
257
|
execute: async (ctx, args) => {
|
|
248
258
|
const stateFilter = args?.trim() || undefined;
|
|
259
|
+
if (stateFilter === 'html') {
|
|
260
|
+
const artifact = await ownerTaskReport(ctx);
|
|
261
|
+
await ctx.replyHtml(artifact.metadata.filename, artifact.html);
|
|
262
|
+
await ctx.reply(`HTML report attached: ${artifact.metadata.filename}`);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (stateFilter && /\s/u.test(stateFilter))
|
|
266
|
+
throw new OwnerCommandUsageError('usage: /tasks [state|html]');
|
|
249
267
|
const tasks = ctx.listTasks(stateFilter && stateFilter !== 'all' ? { state: stateFilter } : undefined);
|
|
250
268
|
await ctx.reply(renderMarkdownList({
|
|
251
269
|
icon: '📋', title: 'Tasks', empty: 'No tasks found.',
|
|
@@ -309,6 +327,9 @@ export const ownerCommands = [
|
|
|
309
327
|
break;
|
|
310
328
|
}
|
|
311
329
|
case 'list': {
|
|
330
|
+
if (rest.some(value => value.startsWith('--')
|
|
331
|
+
&& value !== '--group-by-list' && !value.startsWith('--list=')))
|
|
332
|
+
throw new OwnerCommandUsageError('usage: /task list [state] [--list=<name>] [--group-by-list]');
|
|
312
333
|
const listFlag = rest.find(r => r.startsWith('--list='));
|
|
313
334
|
const list = trailingLines ?? listFlag?.slice('--list='.length);
|
|
314
335
|
const grouped = rest.includes('--group-by-list');
|
|
@@ -345,6 +366,8 @@ export const ownerCommands = [
|
|
|
345
366
|
break;
|
|
346
367
|
}
|
|
347
368
|
case 'lists': {
|
|
369
|
+
if (rest.length || trailingLines)
|
|
370
|
+
throw new OwnerCommandUsageError('usage: /task lists');
|
|
348
371
|
const lists = ctx.listTaskLists();
|
|
349
372
|
await ctx.reply(renderMarkdownList({ icon: '📚', title: 'Task lists', empty: 'No task lists found.',
|
|
350
373
|
records: lists.map(list => `${markdownCode(list.name)}${list.built_in ? ' — built-in' : ''}`) }));
|
|
@@ -388,7 +411,7 @@ export const ownerCommands = [
|
|
|
388
411
|
break;
|
|
389
412
|
}
|
|
390
413
|
case 'show': {
|
|
391
|
-
if (
|
|
414
|
+
if (rest.length !== 1 || rest[0].startsWith('--'))
|
|
392
415
|
throw new OwnerCommandUsageError('usage: /task show <id>');
|
|
393
416
|
await showTask(rest[0]);
|
|
394
417
|
break;
|
|
@@ -438,11 +461,7 @@ export const ownerCommands = [
|
|
|
438
461
|
case 'delete': {
|
|
439
462
|
if (rest.length !== 2 || rest[0] !== rest[1])
|
|
440
463
|
throw new OwnerCommandUsageError('destructive: /task delete <id> <id> — provide the task ID twice');
|
|
441
|
-
|
|
442
|
-
await ctx.reply(renderMarkdownResult({
|
|
443
|
-
icon: '🗑️', title: deleted ? 'Task deleted' : 'Task already absent',
|
|
444
|
-
fields: [{ label: 'ID', value: rest[0], kind: 'code' }],
|
|
445
|
-
}));
|
|
464
|
+
await ctx.deleteTask(rest[0]);
|
|
446
465
|
break;
|
|
447
466
|
}
|
|
448
467
|
case 'recover': {
|
|
@@ -598,10 +617,7 @@ export const ownerCommands = [
|
|
|
598
617
|
const templates = ctx.listTemplateQueries();
|
|
599
618
|
if (!templates.length)
|
|
600
619
|
return ctx.reply('📐 No templates.');
|
|
601
|
-
const lines = templates.map(t => {
|
|
602
|
-
const tag = t.builtin ? ' (built-in)' : '';
|
|
603
|
-
return `${t.name}@${t.version}${tag} ${t.description}`;
|
|
604
|
-
});
|
|
620
|
+
const lines = templates.map(t => `${t.name}@${t.version} ${t.description} [file: ${t.sourceFile ?? 'manifest'}]`);
|
|
605
621
|
await ctx.reply(`📐 Templates:\n${lines.join('\n')}`);
|
|
606
622
|
}),
|
|
607
623
|
},
|
|
@@ -627,10 +643,11 @@ export const ownerCommands = [
|
|
|
627
643
|
const lines = [
|
|
628
644
|
`📐 Template: ${t.name}@${t.version}`,
|
|
629
645
|
`Description: ${t.description}`,
|
|
630
|
-
|
|
646
|
+
`Source: ${t.sourceFile ?? 'manifest'}`,
|
|
631
647
|
...(t.contract ? [`Contract: ${t.contract}`] : []),
|
|
632
648
|
'Members:',
|
|
633
|
-
...t.members.map(m => ` ${m.slot} (${m.role}) ×${m.count} →
|
|
649
|
+
...t.members.map(m => ` ${m.slot} (${m.role}) ×${m.count} → `
|
|
650
|
+
+ `Agent Template: ${m.agent_template}`),
|
|
634
651
|
];
|
|
635
652
|
await ctx.reply(lines.join('\n'));
|
|
636
653
|
};
|
|
@@ -645,10 +662,7 @@ export const ownerCommands = [
|
|
|
645
662
|
const templates = ctx.listTemplateQueries();
|
|
646
663
|
if (!templates.length)
|
|
647
664
|
return ctx.reply('📐 No templates.');
|
|
648
|
-
const lines = templates.map(t => {
|
|
649
|
-
const tag = t.builtin ? ' (built-in)' : '';
|
|
650
|
-
return `${t.name}@${t.version}${tag} ${t.description}`;
|
|
651
|
-
});
|
|
665
|
+
const lines = templates.map(t => `${t.name}@${t.version} ${t.description} [file: ${t.sourceFile ?? 'manifest'}]`);
|
|
652
666
|
await ctx.reply(`📐 Templates:\n${lines.join('\n')}`);
|
|
653
667
|
break;
|
|
654
668
|
}
|
|
@@ -716,6 +730,7 @@ export function fleetCliOps(role, configPath) {
|
|
|
716
730
|
}),
|
|
717
731
|
closeRoom: roomId => launchFleetWorker(['room', '_delete', roomId], `room-delete-${roomId}`, configPath),
|
|
718
732
|
settleTask: taskId => launchFleetWorker(['task', '_settle', taskId], `task-settle-${taskId}`, configPath),
|
|
733
|
+
settleTaskDeletion: taskId => launchFleetWorker(['task', '_settle_delete', taskId], `task-delete-${taskId}`, configPath),
|
|
719
734
|
recoverTask: taskId => launchFleetWorker(['task', '_recover', taskId], `task-recover-${taskId}`, configPath),
|
|
720
735
|
};
|
|
721
736
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** A send whose (dedupe-scoped) digest was already recorded: it must not repeat. */
|
|
2
2
|
export declare class DuplicateSendError extends Error {
|
|
3
|
+
readonly status: OwnerProactiveSend['status'];
|
|
4
|
+
constructor(status: OwnerProactiveSend['status']);
|
|
3
5
|
}
|
|
4
6
|
/** Durable bounded dedupe containing wire IDs only — never message or reply plaintext. */
|
|
5
7
|
export declare class OwnerChannelState {
|
|
@@ -5,6 +5,11 @@ import { replaceFileAtomically } from '../atomic-file.js';
|
|
|
5
5
|
import { canonicalCid } from '../config.js';
|
|
6
6
|
/** A send whose (dedupe-scoped) digest was already recorded: it must not repeat. */
|
|
7
7
|
export class DuplicateSendError extends Error {
|
|
8
|
+
status;
|
|
9
|
+
constructor(status) {
|
|
10
|
+
super('duplicate proactive owner message refused');
|
|
11
|
+
this.status = status;
|
|
12
|
+
}
|
|
8
13
|
}
|
|
9
14
|
/** Durable bounded dedupe containing wire IDs only — never message or reply plaintext. */
|
|
10
15
|
export class OwnerChannelState {
|
|
@@ -184,8 +189,9 @@ export class OwnerConversationState {
|
|
|
184
189
|
// 'all' scope serves wire-keyed idempotency: a crash replay must not
|
|
185
190
|
// deliver the same wire to a second owner after the route moved.
|
|
186
191
|
const scope = dedupe === 'all' ? this.sends : recent;
|
|
187
|
-
|
|
188
|
-
|
|
192
|
+
const duplicate = scope.find(send => send.digest === digest);
|
|
193
|
+
if (duplicate)
|
|
194
|
+
throw new DuplicateSendError(duplicate.status);
|
|
189
195
|
const last = recent.at(-1);
|
|
190
196
|
if (last && now - last.at < minIntervalMs)
|
|
191
197
|
throw new Error(`proactive owner messages are rate-limited to one every ${minIntervalMs}ms`);
|
package/dist/permissions.js
CHANGED
|
@@ -85,7 +85,7 @@ export function analyzeRolePermissions(role) {
|
|
|
85
85
|
translation = { ...translation, capabilities: inspection.capabilities };
|
|
86
86
|
}
|
|
87
87
|
const conflicts = neutral.supported
|
|
88
|
-
? findConflicts(role, neutral.native, adapter.nativePermissionOverrides(role.harness_options))
|
|
88
|
+
? findConflicts(role, neutral.native, adapter.nativePermissionOverrides(role.harness_options, role))
|
|
89
89
|
: [];
|
|
90
90
|
const floor = checkUnattendedFloor(translation.capabilities, requiredUnattendedFloor(role));
|
|
91
91
|
const floorSeverity = role.permissions.unattended === 'deny' ? 'fail' : 'warn';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface BootstrapResult {
|
|
2
|
+
revision: number;
|
|
3
|
+
sourceRoot: string;
|
|
4
|
+
configPath: string;
|
|
5
|
+
created: string[];
|
|
6
|
+
preserved: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare const packagedPresetRoot: () => string;
|
|
9
|
+
/** Seed packaged defaults without replacing a single existing byte. */
|
|
10
|
+
export declare function bootstrapPresets(configuration?: string): BootstrapResult;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { constants, copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, chmodSync, } from 'node:fs';
|
|
2
|
+
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { defaultConfigPath } from './paths.js';
|
|
5
|
+
import { splitRootFor } from './config.js';
|
|
6
|
+
export const packagedPresetRoot = () => fileURLToPath(new URL('../presets', import.meta.url));
|
|
7
|
+
function assertSafeExisting(path, kind) {
|
|
8
|
+
const stat = lstatSync(path);
|
|
9
|
+
if (stat.isSymbolicLink())
|
|
10
|
+
throw new Error(`preset bootstrap refuses symlink target: ${path}`);
|
|
11
|
+
if (kind === 'file' ? !stat.isFile() : !stat.isDirectory())
|
|
12
|
+
throw new Error(`preset bootstrap expected ${kind}: ${path}`);
|
|
13
|
+
const uid = process.getuid?.();
|
|
14
|
+
if (uid !== undefined && stat.uid !== uid)
|
|
15
|
+
throw new Error(`preset bootstrap refuses target owned by uid ${stat.uid}: ${path}`);
|
|
16
|
+
if ((stat.mode & 0o022) !== 0)
|
|
17
|
+
throw new Error(`preset bootstrap refuses group/world-writable target: ${path}`);
|
|
18
|
+
}
|
|
19
|
+
function ensureDirectory(path) {
|
|
20
|
+
const parent = dirname(path);
|
|
21
|
+
if (parent !== path && !existsSync(parent))
|
|
22
|
+
ensureDirectory(parent);
|
|
23
|
+
if (existsSync(path)) {
|
|
24
|
+
assertSafeExisting(path, 'directory');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
mkdirSync(path, { mode: 0o700 });
|
|
28
|
+
}
|
|
29
|
+
function seedFile(source, target, result) {
|
|
30
|
+
ensureDirectory(dirname(target));
|
|
31
|
+
if (existsSync(target)) {
|
|
32
|
+
assertSafeExisting(target, 'file');
|
|
33
|
+
result.preserved.push(target);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
copyFileSync(source, target, constants.COPYFILE_EXCL);
|
|
38
|
+
chmodSync(target, 0o600);
|
|
39
|
+
result.created.push(target);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (error.code !== 'EEXIST')
|
|
43
|
+
throw error;
|
|
44
|
+
assertSafeExisting(target, 'file');
|
|
45
|
+
result.preserved.push(target);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function presetFiles(root) {
|
|
49
|
+
const files = [];
|
|
50
|
+
const visit = (directory) => {
|
|
51
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
52
|
+
const path = join(directory, entry.name);
|
|
53
|
+
if (entry.isDirectory())
|
|
54
|
+
visit(path);
|
|
55
|
+
else if (entry.isFile())
|
|
56
|
+
files.push(path);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
visit(root);
|
|
60
|
+
return files.sort();
|
|
61
|
+
}
|
|
62
|
+
/** Seed packaged defaults without replacing a single existing byte. */
|
|
63
|
+
export function bootstrapPresets(configuration = defaultConfigPath()) {
|
|
64
|
+
const sourceRoot = packagedPresetRoot();
|
|
65
|
+
const manifest = JSON.parse(readFileSync(join(sourceRoot, 'manifest.json'), 'utf8'));
|
|
66
|
+
if (manifest.schema_version !== 1 || !Number.isInteger(manifest.preset_revision))
|
|
67
|
+
throw new Error(`invalid packaged preset manifest: ${join(sourceRoot, 'manifest.json')}`);
|
|
68
|
+
const configPath = resolve(configuration);
|
|
69
|
+
const result = {
|
|
70
|
+
revision: manifest.preset_revision, sourceRoot, configPath, created: [], preserved: [],
|
|
71
|
+
};
|
|
72
|
+
ensureDirectory(dirname(configPath));
|
|
73
|
+
seedFile(join(sourceRoot, 'fleet.yaml'), configPath, result);
|
|
74
|
+
const sourceSplit = join(sourceRoot, 'fleet');
|
|
75
|
+
const targetSplit = splitRootFor(configPath);
|
|
76
|
+
if (existsSync(targetSplit))
|
|
77
|
+
assertSafeExisting(targetSplit, 'directory');
|
|
78
|
+
for (const source of presetFiles(sourceSplit)) {
|
|
79
|
+
const rel = relative(sourceSplit, source);
|
|
80
|
+
if (rel.startsWith('..') || rel.includes(`..${sep}`))
|
|
81
|
+
throw new Error(`invalid packaged preset path: ${source}`);
|
|
82
|
+
seedFile(source, join(targetSplit, rel), result);
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { renameSync } from 'node:fs';
|
|
2
|
+
export declare const legacyStarterMigrationManifest: Readonly<{
|
|
3
|
+
schemaVersion: 1;
|
|
4
|
+
presetRevision: 2;
|
|
5
|
+
fingerprints: Readonly<Record<string, string>>;
|
|
6
|
+
}>;
|
|
7
|
+
export interface PresetMigrationResult {
|
|
8
|
+
write: boolean;
|
|
9
|
+
root: string;
|
|
10
|
+
stagingPath: string;
|
|
11
|
+
backupPath: string;
|
|
12
|
+
moves: Array<{
|
|
13
|
+
from: string;
|
|
14
|
+
to: string;
|
|
15
|
+
}>;
|
|
16
|
+
additions: string[];
|
|
17
|
+
}
|
|
18
|
+
export interface RoleDefaultMigrationResult {
|
|
19
|
+
write: boolean;
|
|
20
|
+
root: string;
|
|
21
|
+
stagingPath: string;
|
|
22
|
+
backupPath: string;
|
|
23
|
+
removals: string[];
|
|
24
|
+
replacements: string[];
|
|
25
|
+
additions: string[];
|
|
26
|
+
preserved: string[];
|
|
27
|
+
}
|
|
28
|
+
interface MigrationHooks {
|
|
29
|
+
rename?: typeof renameSync;
|
|
30
|
+
nonce?: string;
|
|
31
|
+
beforeLockClaim?: () => void;
|
|
32
|
+
beforeRoleDefaultPublish?: () => void;
|
|
33
|
+
}
|
|
34
|
+
/** Explicit adoption of exact packaged revision-3 role defaults; customized files are never changed. */
|
|
35
|
+
export declare function migratePackagedRoleDefaults(configuration: string, options?: {
|
|
36
|
+
write?: boolean;
|
|
37
|
+
}, hooks?: MigrationHooks): RoleDefaultMigrationResult;
|
|
38
|
+
/** Explicit safe migration of the exact revision-2 six-worker starter set. Dry-run unless write=true. */
|
|
39
|
+
export declare function migrateLegacyStarterPresets(configuration: string, options?: {
|
|
40
|
+
write?: boolean;
|
|
41
|
+
}, hooks?: MigrationHooks): PresetMigrationResult;
|
|
42
|
+
export {};
|