@ours.network/install 1.2.1-nightly.33 → 1.2.1-nightly.35
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 +22 -2
- package/assets/release-lock.json +12 -12
- package/assets/release.json +7 -7
- package/assets/scripts/runtime/notifications-gateway.mjs +2 -2
- package/assets/sources.json +10 -10
- package/lib/effects.mjs +7 -0
- package/lib/orchestrate.mjs +57 -1
- package/lib/setup-options.mjs +11 -1
- package/lib/setup.mjs +2 -1
- package/lib/usage.mjs +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,19 @@ This permanent command contains no tunnel credentials or enrollment expiry. On a
|
|
|
8
8
|
|
|
9
9
|
`--disable-fleet-agents-setup` installs the Fleet CLI and saves the choice without opening its agent/model wizard or rewriting existing Fleet configuration. Configure agents in the App after device linking. Complete presets still work unattended; partial presets require a terminal and cannot use `OURS_ASSUME_YES`.
|
|
10
10
|
|
|
11
|
+
### Fleet task commands from a harness command sandbox
|
|
12
|
+
|
|
13
|
+
After Fleet configuration, and again on update, the installer asks the exact Fleet it installed to run `ours-fleet managed-cli setup`. Fleet owns that policy; the installer writes no Codex rule or Claude setting itself, and the step starts no agent session and no model. It runs only when the installed Fleet declares the `managed-cli.setup-v1` capability; the Fleet release this installer pins does. With an earlier Fleet (a retained or development installation) it is skipped (and `--fleet-task-workflow` reports that it was not applied), and it never fails an installation: an unsupported combination is printed with Fleet's reason and left exactly as sandboxed as before.
|
|
14
|
+
|
|
15
|
+
Nothing is prepared unless an Agent declares `managed_cli: [task-workflow]`. To opt agents in from the installer, name them:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
ours-install client --config /private/profile.json --integrations codex,fleet \
|
|
19
|
+
--fleet-settings /private/fleet-settings.json --fleet-task-workflow FleetCoordinator
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`--fleet-task-workflow` takes a comma-separated list of Fleet agent names, requires the Fleet integration and conflicts with `--disable-fleet-agents-setup`. It adds that one key to each named Agent and changes no permission. The prepared scope is the packaged Coordinator task workflow (`task create|start|finish|block|unblock|review|list|show`, `room show|members`, template/plan/help inspection) for one pinned Fleet configuration; `task start` and `task finish` provision and retire rooms and agents, which is real authority, and the installer prints that scope. The result is generated configuration only. It is not evidence that a command reached a supervisor: `ours-fleet managed-cli status` and `ours-fleet doctor` report what running agents actually observed. Fleet's `docs/validation/managed-cli-permissions.md` lists the qualified harnesses and platforms.
|
|
23
|
+
|
|
11
24
|
The separate `ours-fleet setup-tunnel <encoded-v2-grant>` step is supported by the pinned Fleet release. Its private App payload remains scoped, signed-root-bound and expiring. Fleet prints its QR and private single-use connection code immediately after successful setup; scan or paste it into the App. `ours-fleet link-device` remains available for additional devices or expired codes. Do not put the expiring tunnel payload in the permanent installer command.
|
|
12
25
|
|
|
13
26
|
Native packages currently provide server-only installation; full Fleet/App onboarding requires the supported gateway (Docker or Podman). The installer refuses unsupported native full-stack setup before changing anything. This flow does not add a native gateway.
|
|
@@ -624,7 +637,7 @@ Existing installation records keep their ports and identities. A listener acquir
|
|
|
624
637
|
by another process after preflight still causes startup to fail safely; no foreign
|
|
625
638
|
process is stopped and no retained installation is silently moved.
|
|
626
639
|
|
|
627
|
-
Workspace setup pins Fleet `1.2.0-nightly.
|
|
640
|
+
Workspace setup pins Fleet `1.2.0-nightly.46` with its recorded registry integrity. This release includes first-time Fleet setup through the host API for the account App, direct App access to the workspace services with the device credential, v2 positional one-use grants and automatic QR/connection-code output from `ours-fleet setup-tunnel`, optional private file/stdin inputs for legacy v1 payloads, a separate `ours-fleet link-device` command for additional devices, host prerequisite checks before the one-time workspace proof (`setup-tunnel --check`), `setup-tunnel --resume` to finish a setup that stopped after the host saved its private pending setup record (not when the proof's answer was lost, or after the setup window expired with an unconfirmed non-default port), and persists the available Fleet loopback port used by the workspace tunnel. It also accepts a setup-generated configuration left behind after the host enrollment state was removed, runs the host Codex when it is at least as new as the packaged one, offers only the Codex models the signed-in account can use, and removes the temporary setup contact after confirmation. Fleet serves no pages and opens no browser: every screen is in the App. Fleet also serves the workspace notification routes, produces notifications through the server gateway with the imported producer credential, accepts the App's device credential on the notification presence socket so an open App receives no push, and defaults agent wakes to `monitor.interrupt: after_tool` (Hermes: no interrupt; role snapshots saved before this default keep no interrupt). Existing installation identities and configured ports remain retained; a retained-port collision fails safely.
|
|
628
641
|
|
|
629
642
|
### App tunnel setup prerelease sequencing
|
|
630
643
|
|
|
@@ -641,10 +654,17 @@ connection code automatically; the App checks readiness and opens the code field
|
|
|
641
654
|
Private `--file`/`--stdin` transports remain optional and are the only transports
|
|
642
655
|
for legacy v1 payloads with long-lived credentials.
|
|
643
656
|
|
|
644
|
-
The nightly installer release manifest selects published Fleet `1.2.0-nightly.
|
|
657
|
+
The nightly installer release manifest selects published Fleet `1.2.0-nightly.46`
|
|
645
658
|
and its actual registry integrity through the normal release manifest/source-policy
|
|
646
659
|
generators. This release includes v2 argument and private-input support. The former
|
|
647
660
|
nightly.41 selection predates this capability. Merge and publish the repinned
|
|
648
661
|
installer before deploying the App; the pin change alone is not an installer
|
|
649
662
|
publication. Existing v1 private-file enrollment remains compatible. The legacy
|
|
650
663
|
App wrapper refuses an older Fleet before reading or redeeming its grant.
|
|
664
|
+
|
|
665
|
+
The notification gateway permits daemon-authenticated producer selection only on
|
|
666
|
+
POST `/api/v1/send` and `/api/v1/delete-target`. The latter retires a removed
|
|
667
|
+
conversation using the credential's fixed user/source scope. Browser inbox
|
|
668
|
+
`/api/v1/delete` continues to require the user credential. The notification
|
|
669
|
+
service must support the scoped lifecycle route; this change does not repin or
|
|
670
|
+
publish an installer release.
|
package/assets/release-lock.json
CHANGED
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
"@ours.network/codex": "1.2.0-nightly.10",
|
|
14
14
|
"@ours.network/cowork": "1.3.3-nightly.20261001.6d49d3a",
|
|
15
15
|
"@ours.network/daemon": "3.8.1-nightly.7",
|
|
16
|
-
"@ours.network/fleet": "1.2.0-nightly.
|
|
16
|
+
"@ours.network/fleet": "1.2.0-nightly.46",
|
|
17
17
|
"@ours.network/mcp": "1.2.0-nightly.10",
|
|
18
|
-
"@ours.network/messenger-server": "1.0.31-nightly.
|
|
19
|
-
"@ours.network/notifications": "0.3.1-nightly.
|
|
18
|
+
"@ours.network/messenger-server": "1.0.31-nightly.15",
|
|
19
|
+
"@ours.network/notifications": "0.3.1-nightly.2",
|
|
20
20
|
"@ours.network/sdk": "3.8.1-nightly.15",
|
|
21
21
|
"@ours.network/tg-connector": "1.0.1-nightly.9"
|
|
22
22
|
}
|
|
@@ -1001,9 +1001,9 @@
|
|
|
1001
1001
|
}
|
|
1002
1002
|
},
|
|
1003
1003
|
"node_modules/@ours.network/fleet": {
|
|
1004
|
-
"version": "1.2.0-nightly.
|
|
1005
|
-
"resolved": "https://registry.npmjs.org/@ours.network/fleet/-/fleet-1.2.0-nightly.
|
|
1006
|
-
"integrity": "sha512-
|
|
1004
|
+
"version": "1.2.0-nightly.46",
|
|
1005
|
+
"resolved": "https://registry.npmjs.org/@ours.network/fleet/-/fleet-1.2.0-nightly.46.tgz",
|
|
1006
|
+
"integrity": "sha512-k39gR9pKGPb6V4c2nhmfZAuHZmI9jgW+Hd1cTjDydJm7feRbnILTuEKxn5E+EUOxl/NwMIpWEJgCx99P+yQGtA==",
|
|
1007
1007
|
"license": "FSL-1.1-Apache-2.0",
|
|
1008
1008
|
"dependencies": {
|
|
1009
1009
|
"@agentclientprotocol/sdk": "^1.3.0",
|
|
@@ -1047,9 +1047,9 @@
|
|
|
1047
1047
|
}
|
|
1048
1048
|
},
|
|
1049
1049
|
"node_modules/@ours.network/messenger-server": {
|
|
1050
|
-
"version": "1.0.31-nightly.
|
|
1051
|
-
"resolved": "https://registry.npmjs.org/@ours.network/messenger-server/-/messenger-server-1.0.31-nightly.
|
|
1052
|
-
"integrity": "sha512-
|
|
1050
|
+
"version": "1.0.31-nightly.15",
|
|
1051
|
+
"resolved": "https://registry.npmjs.org/@ours.network/messenger-server/-/messenger-server-1.0.31-nightly.15.tgz",
|
|
1052
|
+
"integrity": "sha512-NEpPsn3x4ZxYjhTo3c8JSYPKgfkhZ/W9s0u5Bd6OALWj40qc5ethui1uHfpw5Cu3+XHJz28WPh5e4RbjUMCmXQ==",
|
|
1053
1053
|
"license": "FSL-1.1-Apache-2.0",
|
|
1054
1054
|
"dependencies": {
|
|
1055
1055
|
"@eslint-community/regexpp": "4.12.2",
|
|
@@ -1066,9 +1066,9 @@
|
|
|
1066
1066
|
}
|
|
1067
1067
|
},
|
|
1068
1068
|
"node_modules/@ours.network/notifications": {
|
|
1069
|
-
"version": "0.3.1-nightly.
|
|
1070
|
-
"resolved": "https://registry.npmjs.org/@ours.network/notifications/-/notifications-0.3.1-nightly.
|
|
1071
|
-
"integrity": "sha512-
|
|
1069
|
+
"version": "0.3.1-nightly.2",
|
|
1070
|
+
"resolved": "https://registry.npmjs.org/@ours.network/notifications/-/notifications-0.3.1-nightly.2.tgz",
|
|
1071
|
+
"integrity": "sha512-OjnrJ8ZuOfKYT4KAmiRLU7czfiuacs6rShwOvvqxqBaUKcs1UwjsYwHARNb6erMxG4am3xxcE0ItahL+d2vt4A==",
|
|
1072
1072
|
"license": "FSL-1.1-Apache-2.0",
|
|
1073
1073
|
"dependencies": {
|
|
1074
1074
|
"web-push": "3.6.7",
|
package/assets/release.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": 1,
|
|
3
3
|
"channel": "nightly",
|
|
4
|
-
"installerVersion": "1.2.1-nightly.
|
|
4
|
+
"installerVersion": "1.2.1-nightly.35",
|
|
5
5
|
"packages": {
|
|
6
6
|
"@ours.network/sdk": {
|
|
7
7
|
"version": "3.8.1-nightly.15",
|
|
@@ -20,16 +20,16 @@
|
|
|
20
20
|
"integrity": "sha512-tqoLOWc4O8CGwLdO1j6ukm6Vn57EE0zbuxKo4BWd0QVwu9ObKgHCEMOKPgW2mXHeESXSv9m7edcIrnK8I5gD+w=="
|
|
21
21
|
},
|
|
22
22
|
"@ours.network/messenger-server": {
|
|
23
|
-
"version": "1.0.31-nightly.
|
|
24
|
-
"integrity": "sha512-
|
|
23
|
+
"version": "1.0.31-nightly.15",
|
|
24
|
+
"integrity": "sha512-NEpPsn3x4ZxYjhTo3c8JSYPKgfkhZ/W9s0u5Bd6OALWj40qc5ethui1uHfpw5Cu3+XHJz28WPh5e4RbjUMCmXQ=="
|
|
25
25
|
},
|
|
26
26
|
"@ours.network/notifications": {
|
|
27
|
-
"version": "0.3.1-nightly.
|
|
28
|
-
"integrity": "sha512-
|
|
27
|
+
"version": "0.3.1-nightly.2",
|
|
28
|
+
"integrity": "sha512-OjnrJ8ZuOfKYT4KAmiRLU7czfiuacs6rShwOvvqxqBaUKcs1UwjsYwHARNb6erMxG4am3xxcE0ItahL+d2vt4A=="
|
|
29
29
|
},
|
|
30
30
|
"@ours.network/fleet": {
|
|
31
|
-
"version": "1.2.0-nightly.
|
|
32
|
-
"integrity": "sha512-
|
|
31
|
+
"version": "1.2.0-nightly.46",
|
|
32
|
+
"integrity": "sha512-k39gR9pKGPb6V4c2nhmfZAuHZmI9jgW+Hd1cTjDydJm7feRbnILTuEKxn5E+EUOxl/NwMIpWEJgCx99P+yQGtA=="
|
|
33
33
|
},
|
|
34
34
|
"@ours.network/mcp": {
|
|
35
35
|
"version": "1.2.0-nightly.10",
|
|
@@ -21,7 +21,7 @@ export function daemonCredentialCheck(daemonUrl) {
|
|
|
21
21
|
/**
|
|
22
22
|
* Front the notification service with the server's own authentication.
|
|
23
23
|
* A request carrying a server API credential is checked with the daemon; only then
|
|
24
|
-
* does it act as this installation's owner (or, for
|
|
24
|
+
* does it act as this installation's owner (or, for producer lifecycle routes, as the named
|
|
25
25
|
* producer). A request without one keeps its own Authorization, which the service
|
|
26
26
|
* checks against its scoped tokens. Nothing is injected for an unauthenticated caller.
|
|
27
27
|
*/
|
|
@@ -33,7 +33,7 @@ export function createNotificationsAdapter({ service, userToken, verifyServerCre
|
|
|
33
33
|
if (credential === undefined) return producer === undefined;
|
|
34
34
|
if (typeof credential !== 'string' || !credential || !await verifyServerCredential(credential)) return false;
|
|
35
35
|
const path = new URL(req.url ?? '/', 'http://localhost').pathname;
|
|
36
|
-
if (producer !== undefined && !(req.method === 'POST' &&
|
|
36
|
+
if (producer !== undefined && !(req.method === 'POST' && ['/api/v1/send', '/api/v1/delete-target'].includes(path))) return false;
|
|
37
37
|
req.headers.authorization = `Bearer ${producer ?? userToken}`;
|
|
38
38
|
return true;
|
|
39
39
|
};
|
package/assets/sources.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"release": {
|
|
3
3
|
"schema": 1,
|
|
4
4
|
"channel": "nightly",
|
|
5
|
-
"installerVersion": "1.2.1-nightly.
|
|
5
|
+
"installerVersion": "1.2.1-nightly.35",
|
|
6
6
|
"packages": {
|
|
7
7
|
"@ours.network/sdk": {
|
|
8
8
|
"version": "3.8.1-nightly.15",
|
|
@@ -21,16 +21,16 @@
|
|
|
21
21
|
"integrity": "sha512-tqoLOWc4O8CGwLdO1j6ukm6Vn57EE0zbuxKo4BWd0QVwu9ObKgHCEMOKPgW2mXHeESXSv9m7edcIrnK8I5gD+w=="
|
|
22
22
|
},
|
|
23
23
|
"@ours.network/messenger-server": {
|
|
24
|
-
"version": "1.0.31-nightly.
|
|
25
|
-
"integrity": "sha512-
|
|
24
|
+
"version": "1.0.31-nightly.15",
|
|
25
|
+
"integrity": "sha512-NEpPsn3x4ZxYjhTo3c8JSYPKgfkhZ/W9s0u5Bd6OALWj40qc5ethui1uHfpw5Cu3+XHJz28WPh5e4RbjUMCmXQ=="
|
|
26
26
|
},
|
|
27
27
|
"@ours.network/notifications": {
|
|
28
|
-
"version": "0.3.1-nightly.
|
|
29
|
-
"integrity": "sha512-
|
|
28
|
+
"version": "0.3.1-nightly.2",
|
|
29
|
+
"integrity": "sha512-OjnrJ8ZuOfKYT4KAmiRLU7czfiuacs6rShwOvvqxqBaUKcs1UwjsYwHARNb6erMxG4am3xxcE0ItahL+d2vt4A=="
|
|
30
30
|
},
|
|
31
31
|
"@ours.network/fleet": {
|
|
32
|
-
"version": "1.2.0-nightly.
|
|
33
|
-
"integrity": "sha512-
|
|
32
|
+
"version": "1.2.0-nightly.46",
|
|
33
|
+
"integrity": "sha512-k39gR9pKGPb6V4c2nhmfZAuHZmI9jgW+Hd1cTjDydJm7feRbnILTuEKxn5E+EUOxl/NwMIpWEJgCx99P+yQGtA=="
|
|
34
34
|
},
|
|
35
35
|
"@ours.network/mcp": {
|
|
36
36
|
"version": "1.2.0-nightly.10",
|
|
@@ -79,15 +79,15 @@
|
|
|
79
79
|
},
|
|
80
80
|
"@ours.network/messenger-server": {
|
|
81
81
|
"type": "npm",
|
|
82
|
-
"version": "1.0.31-nightly.
|
|
82
|
+
"version": "1.0.31-nightly.15"
|
|
83
83
|
},
|
|
84
84
|
"@ours.network/notifications": {
|
|
85
85
|
"type": "npm",
|
|
86
|
-
"version": "0.3.1-nightly.
|
|
86
|
+
"version": "0.3.1-nightly.2"
|
|
87
87
|
},
|
|
88
88
|
"@ours.network/fleet": {
|
|
89
89
|
"type": "npm",
|
|
90
|
-
"version": "1.2.0-nightly.
|
|
90
|
+
"version": "1.2.0-nightly.46"
|
|
91
91
|
},
|
|
92
92
|
"@ours.network/mcp": {
|
|
93
93
|
"type": "npm",
|
package/lib/effects.mjs
CHANGED
|
@@ -1436,6 +1436,13 @@ export function networkEffects(effects) {
|
|
|
1436
1436
|
const localPackages = Object.fromEntries(integrations.filter(n => n !== 'fleet').map(name => [name, join(root, 'node_modules', '@ours.network', name)]));
|
|
1437
1437
|
return { localPackages, packages: {}, cliBin, fleetBin: integrations.includes('fleet') ? join(root, 'node_modules/.bin/ours-fleet') : null };
|
|
1438
1438
|
},
|
|
1439
|
+
/** Capability tokens the given Fleet executable reports for itself; [] when it reports none. */
|
|
1440
|
+
async fleetCapabilities(fleetBin, env = null) {
|
|
1441
|
+
try {
|
|
1442
|
+
const capabilities = JSON.parse((await effects.run(fleetBin, ['version', '--json'], { env })).stdout).capabilities;
|
|
1443
|
+
return Array.isArray(capabilities) ? capabilities.filter(item => typeof item === 'string') : [];
|
|
1444
|
+
} catch { return []; }
|
|
1445
|
+
},
|
|
1439
1446
|
async prepareClientMarketplace(name, packagePath) {
|
|
1440
1447
|
const acquisitionRoot = dirname(dirname(dirname(packagePath)));
|
|
1441
1448
|
const sourcePath = join(acquisitionRoot, 'sources.json');
|
package/lib/orchestrate.mjs
CHANGED
|
@@ -993,6 +993,58 @@ export async function runHarnessPhase(args, effects, { target, isDefaultStateDir
|
|
|
993
993
|
return rows;
|
|
994
994
|
}
|
|
995
995
|
|
|
996
|
+
export const FLEET_MANAGED_CLI_CAPABILITY = 'managed-cli.setup-v1';
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
* Ask the Fleet release that was just installed to prepare and validate its own
|
|
1000
|
+
* managed CLI policy. Fleet owns the commands, paths and harness rules; nothing
|
|
1001
|
+
* about them is duplicated here. The command is static: it starts no agent
|
|
1002
|
+
* session and no model. Its findings are reported, never turned into an
|
|
1003
|
+
* installation failure, and never widened on the operator's behalf.
|
|
1004
|
+
*/
|
|
1005
|
+
export async function runFleetManagedCliSetup(args, effects, plan, target) {
|
|
1006
|
+
const fleet = args.acquiredFleet || plan.init[0];
|
|
1007
|
+
const env = target.mode === 'host-profile' ? profileEnv(target) : daemonEnv(target.stateDir, target.port);
|
|
1008
|
+
const requested = args.fleetTaskWorkflowAgents ?? [];
|
|
1009
|
+
const manual = `${fleet === plan.init[0] ? fleet : shellQuote(fleet)} managed-cli setup --configuration ${shellQuote(plan.configPath)}`;
|
|
1010
|
+
// Version-matched: the capability is asked of the exact Fleet that was installed.
|
|
1011
|
+
const capabilities = await effects.fleetCapabilities(fleet, env);
|
|
1012
|
+
if (!capabilities.includes(FLEET_MANAGED_CLI_CAPABILITY)) {
|
|
1013
|
+
if (requested.length) effects.out(warn(`The installed Fleet does not provide managed CLI setup (${FLEET_MANAGED_CLI_CAPABILITY}); --fleet-task-workflow was not applied to ${requested.join(', ')} and nothing was changed.`));
|
|
1014
|
+
return { state: 'unavailable' };
|
|
1015
|
+
}
|
|
1016
|
+
let report;
|
|
1017
|
+
try {
|
|
1018
|
+
const result = await effects.run(fleet, ['managed-cli', 'setup', '--configuration', plan.configPath, '--json',
|
|
1019
|
+
...requested.flatMap(agent => ['--enable', agent])], { env, allowCodes: [0, 1] });
|
|
1020
|
+
report = JSON.parse(result.stdout ?? '');
|
|
1021
|
+
if (report?.version !== 1 || !Array.isArray(report.roles)) throw new Error('unrecognised report');
|
|
1022
|
+
} catch (error) {
|
|
1023
|
+
effects.out(warn(`Fleet managed CLI setup did not complete (${installerFailure(error)}). Nothing else was changed; retry: ${manual}`));
|
|
1024
|
+
return { state: 'failed' };
|
|
1025
|
+
}
|
|
1026
|
+
for (const item of report.enabled ?? [])
|
|
1027
|
+
effects.out(ok(`${item.changed ? 'Enabled' : 'Already enabled:'} Fleet task commands for ${item.agent} (${item.file}). Its permissions were not changed.`));
|
|
1028
|
+
const declared = report.roles.filter(role => role.setup?.state !== 'not-declared');
|
|
1029
|
+
if (!declared.length) {
|
|
1030
|
+
effects.out(info('No Fleet agent is set up to run Fleet task commands from a command sandbox; nothing was generated and no permission changed.'));
|
|
1031
|
+
effects.out(info(`To allow it for one agent, re-run with --fleet-task-workflow <Agent> or run: ${manual} --enable <Agent>`));
|
|
1032
|
+
return { state: 'none', report };
|
|
1033
|
+
}
|
|
1034
|
+
const good = ['prepared', 'generated-at-launch', 'not-required'];
|
|
1035
|
+
for (const role of declared) {
|
|
1036
|
+
const line = `${role.role} (${role.harness}/${role.session}): ${role.setup.state} — ${role.setup.detail}`;
|
|
1037
|
+
effects.out((good.includes(role.setup.state) ? ok : warn)(`Fleet task commands, ${line}`));
|
|
1038
|
+
for (const scope of role.scope ?? []) effects.out(info(` ${scope}`));
|
|
1039
|
+
for (const note of role.warnings ?? []) effects.out(info(` note: ${note}`));
|
|
1040
|
+
if (role.session_policy === 'restart-required')
|
|
1041
|
+
effects.out(warn(` ${role.role} is still running the previous policy; restart it (ours-fleet restart ${role.role}) to load this one.`));
|
|
1042
|
+
}
|
|
1043
|
+
for (const item of report.removed ?? []) effects.out(info(`Removed obsolete Fleet-generated policy ${item.path} (${item.detail ?? 'no longer needed'}).`));
|
|
1044
|
+
effects.out(info('This is generated configuration only. It was not executed: no agent session or model was started, and it is not evidence that a command reached a supervisor. Check with: ours-fleet doctor'));
|
|
1045
|
+
return { state: declared.every(role => good.includes(role.setup.state)) ? 'prepared' : 'attention', report };
|
|
1046
|
+
}
|
|
1047
|
+
|
|
996
1048
|
/** Install Fleet and let its interactive wizard publish the owned configuration. */
|
|
997
1049
|
export async function runFleetPhase(args, effects, { target, isDefaultStateDir }) {
|
|
998
1050
|
effects.out(heading('ours-fleet (your always-online agent team)'));
|
|
@@ -1012,6 +1064,8 @@ export async function runFleetPhase(args, effects, { target, isDefaultStateDir }
|
|
|
1012
1064
|
if (args.disableFleetAgentsSetup) {
|
|
1013
1065
|
if (!install.ok) return { key: 'fleet', label: plan.label, state: 'failed', note: 'Fleet installation failed' };
|
|
1014
1066
|
effects.out(ok('Fleet CLI installed; agent and model setup deferred to the App. Existing Fleet configuration was retained.'));
|
|
1067
|
+
// A retained configuration may already declare the workflow; its generated policy names the package path this run just replaced.
|
|
1068
|
+
if (!args.dryRun && effects.readText(plan.configPath) !== null) await runFleetManagedCliSetup(args, effects, plan, target);
|
|
1015
1069
|
return { key: 'fleet', label: plan.label, state: 'installed', note: 'agents setup deferred' };
|
|
1016
1070
|
}
|
|
1017
1071
|
// Fleet owns its v2 manifest, roles, models and permissions. With no prepared
|
|
@@ -1037,6 +1091,7 @@ export async function runFleetPhase(args, effects, { target, isDefaultStateDir }
|
|
|
1037
1091
|
return { key: 'fleet', label: plan.label, state: 'failed', note: 'Fleet configuration was not published' };
|
|
1038
1092
|
}
|
|
1039
1093
|
effects.out(ok('ours-fleet installed and initialized; no fleet roles were started.'));
|
|
1094
|
+
if (!args.dryRun) await runFleetManagedCliSetup(args, effects, plan, target);
|
|
1040
1095
|
if (plan.instruction) effects.out(info(plan.instruction));
|
|
1041
1096
|
return { key: 'fleet', label: plan.label, state: 'installed', note: `configured at ${plan.configPath}; stopped` };
|
|
1042
1097
|
}
|
|
@@ -1517,7 +1572,8 @@ export async function runClientCommand(command, effects) {
|
|
|
1517
1572
|
phase = 'Acquire client packages';
|
|
1518
1573
|
const exactSuite = await effects.acquireClientPackages(imported.configPath, imported.settings.sourcesPath, integrations, { refresh: !!command.preset });
|
|
1519
1574
|
const args = { assumeYes: true, dryRun: false, channel: 'latest', clientIntegrations: integrations,
|
|
1520
|
-
acquiredFleet: exactSuite.fleetBin, fleetSettingsPath: imported.settings.fleetSettingsPath, disableFleetAgentsSetup: imported.settings.disableFleetAgentsSetup
|
|
1575
|
+
acquiredFleet: exactSuite.fleetBin, fleetSettingsPath: imported.settings.fleetSettingsPath, disableFleetAgentsSetup: imported.settings.disableFleetAgentsSetup,
|
|
1576
|
+
fleetTaskWorkflowAgents: command.fleetTaskWorkflowAgents };
|
|
1521
1577
|
const target = { mode: 'host-profile', managed: true, configPath: imported.configPath, profile: imported.profile, endpoint: imported.profile.endpoint };
|
|
1522
1578
|
effects.out(progress(2, 4, 'Client integrations', 'Register the selected agent integrations.'));
|
|
1523
1579
|
phase = 'Register client integrations';
|
package/lib/setup-options.mjs
CHANGED
|
@@ -7,7 +7,7 @@ const integrations = ['codex', 'claude-code', 'fleet'];
|
|
|
7
7
|
const valueFlags = new Map(Object.entries({
|
|
8
8
|
'--container-engine': 'containerEngine', '--server-url': 'serverUrl', '--scope': 'scope', '--action': 'operation', '--mode': 'mode', '--state-dir': 'stateDir',
|
|
9
9
|
'--username': 'localUsername', '--name': 'name', '--surname': 'surname', '--hostname': 'hostname',
|
|
10
|
-
'--identity-name': 'identityName', '--integrations': 'integrations', '--fleet-settings': 'fleetSettingsPath',
|
|
10
|
+
'--identity-name': 'identityName', '--integrations': 'integrations', '--fleet-settings': 'fleetSettingsPath', '--fleet-task-workflow': 'fleetTaskWorkflowAgents',
|
|
11
11
|
'--config': 'config', '--sources': 'sources', '--migrate-from': 'migrateFrom', '--port': 'port', '--cowork-port': 'coworkPort', '--messenger-port': 'messengerPort',
|
|
12
12
|
}));
|
|
13
13
|
const boolFlags = new Map([['--compatible', 'compatible'], ['--dry-run', 'dryRun'], ['--migrate', 'migrate'], ['--disable-fleet-agents-setup', 'disableFleetAgentsSetup']]);
|
|
@@ -54,6 +54,15 @@ export function validateSetupOptions(input, { interactive = input?.interactive =
|
|
|
54
54
|
} else if (options.hostname !== undefined) throw new Error('--hostname requires username, name and surname');
|
|
55
55
|
if (options.disableFleetAgentsSetup !== undefined && typeof options.disableFleetAgentsSetup !== 'boolean') throw new Error('disableFleetAgentsSetup must be a boolean');
|
|
56
56
|
if (options.disableFleetAgentsSetup && options.fleetSettingsPath) throw new Error('--disable-fleet-agents-setup conflicts with --fleet-settings');
|
|
57
|
+
if (options.fleetTaskWorkflowAgents !== undefined) {
|
|
58
|
+
// Fleet owns what the workflow is; the installer only carries which agents the operator named.
|
|
59
|
+
const agents = options.fleetTaskWorkflowAgents;
|
|
60
|
+
if (!Array.isArray(agents) || !agents.length || agents.some(name => typeof name !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(name)) || new Set(agents).size !== agents.length)
|
|
61
|
+
throw new Error('--fleet-task-workflow requires unique Fleet agent names separated by commas');
|
|
62
|
+
if (!options.integrations?.includes?.('fleet')) throw new Error('--fleet-task-workflow requires the fleet integration');
|
|
63
|
+
if (options.disableFleetAgentsSetup) throw new Error('--fleet-task-workflow conflicts with --disable-fleet-agents-setup: no agents are configured by this run');
|
|
64
|
+
options.fleetTaskWorkflowAgents = [...agents];
|
|
65
|
+
}
|
|
57
66
|
const missing = [];
|
|
58
67
|
if (server) for (const [key, flag] of [['mode', '--mode'], ['stateDir', '--state-dir'], ...(options.migrateFrom === undefined ? [['identityName', '--identity-name']] : [])]) if (!nonempty(options[key])) missing.push(flag);
|
|
59
68
|
if (client && options.integrations === undefined) missing.push('--integrations (use none to skip)');
|
|
@@ -135,6 +144,7 @@ export function parseSetupArgs(argv, { home, validate = true } = {}) {
|
|
|
135
144
|
if (!nonempty(value) || value.startsWith('--')) throw new Error(`${flag} requires a value`);
|
|
136
145
|
const key = valueFlags.get(flag);
|
|
137
146
|
if (key === 'integrations') put(key, value === 'none' ? [] : value.split(',').map(item => item.trim()));
|
|
147
|
+
else if (key === 'fleetTaskWorkflowAgents') put(key, value.split(',').map(item => item.trim()));
|
|
138
148
|
else if (Object.hasOwn(defaults, key)) {
|
|
139
149
|
if (!/^[1-9]\d*$/.test(value)) throw new Error(`${flag} requires an integer port`);
|
|
140
150
|
put(key, Number(value)); options.explicitPorts.push(key);
|
package/lib/setup.mjs
CHANGED
|
@@ -136,7 +136,8 @@ export async function executeSetupPlan(plan, effects, { server = runServerComman
|
|
|
136
136
|
if (plan.scope !== 'server') {
|
|
137
137
|
const result = await client({ role: 'client', operation: 'install', config: clientConfig,
|
|
138
138
|
integrations: plan.integrations, fleetSettingsPath: plan.fleetSettingsPath, sourcePolicy: clientPolicy,
|
|
139
|
-
disableFleetAgentsSetup: plan.disableFleetAgentsSetup,
|
|
139
|
+
disableFleetAgentsSetup: plan.disableFleetAgentsSetup, fleetTaskWorkflowAgents: plan.fleetTaskWorkflowAgents,
|
|
140
|
+
preset: true, nonInteractive: !plan.interactive }, effects);
|
|
140
141
|
if (result !== 0) return result;
|
|
141
142
|
}
|
|
142
143
|
if (plan.disableFleetAgentsSetup && plan.integrations?.includes('fleet')) effects.out(info('Fleet is installed. Run ours-fleet setup-tunnel with the expiring one-use payload from the App command; a compatible Fleet prints the QR and connection code automatically. Configure agents and models in the App after linking.'));
|
package/lib/usage.mjs
CHANGED
|
@@ -37,6 +37,7 @@ export const USAGE = `ours-install — interactive setup with optional CLI prese
|
|
|
37
37
|
--identity-name NAME desired Human name for a fresh server; existing root is retained
|
|
38
38
|
--integrations LIST codex,claude-code,fleet; use none to skip clients explicitly
|
|
39
39
|
--fleet-settings PATH JSON settings for Fleet; required unless Fleet agents setup is disabled
|
|
40
|
+
--fleet-task-workflow A[,B] let these Fleet agents run Fleet task commands outside their command sandbox
|
|
40
41
|
--config PATH complete connection profile for client-only setup
|
|
41
42
|
--sources PATH explicit full development source policy override
|
|
42
43
|
--port N public gateway port for Docker (default 3050; e.g. 4050)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ours.network/install",
|
|
3
|
-
"version": "1.2.1-nightly.
|
|
3
|
+
"version": "1.2.1-nightly.35",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The all-in-one ours.network installer: one shared daemon, MCP, cowork, Telegram, Fleet initialization, harness plugins, Human identity, progress UI, and guided next steps.",
|
|
6
6
|
"type": "module",
|