unbound-cli 0.9.6 → 0.9.8
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/package.json +1 -1
- package/src/commands/discover.js +17 -4
- package/src/commands/onboard.js +20 -6
- package/src/commands/setup.js +15 -9
- package/test/onboard-cron.test.js +122 -0
- package/test/setup-args.test.js +20 -1
package/package.json
CHANGED
package/src/commands/discover.js
CHANGED
|
@@ -183,6 +183,21 @@ async function runDiscoveryScan({ apiKey, domain }) {
|
|
|
183
183
|
await runDiscoveryScript('install.sh', args);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Installs the recurring 12-hour discovery LaunchAgent (macOS only) via
|
|
188
|
+
* setup-scheduled-scan.sh. Extracted so `unbound onboard --cron` reuses the
|
|
189
|
+
* exact same scheduling path as `unbound discover schedule`. The scheduled job
|
|
190
|
+
* runs the discovery scan only — it never runs tool setup or --backfill.
|
|
191
|
+
*/
|
|
192
|
+
async function runDiscoverySchedule({ apiKey, domain }) {
|
|
193
|
+
if (!apiKey) {
|
|
194
|
+
throw new Error('Discovery API key is required.');
|
|
195
|
+
}
|
|
196
|
+
const resolvedDomain = domain || config.getBaseUrl();
|
|
197
|
+
const args = `--api-key ${shellEscape(apiKey)} --domain ${shellEscape(resolvedDomain)}`;
|
|
198
|
+
await runDiscoveryScript('setup-scheduled-scan.sh', args);
|
|
199
|
+
}
|
|
200
|
+
|
|
186
201
|
function register(program) {
|
|
187
202
|
const discover = program
|
|
188
203
|
.command('discover')
|
|
@@ -266,9 +281,7 @@ Examples:
|
|
|
266
281
|
return;
|
|
267
282
|
}
|
|
268
283
|
|
|
269
|
-
|
|
270
|
-
const args = `--api-key ${shellEscape(opts.apiKey)} --domain ${shellEscape(domain)}`;
|
|
271
|
-
await runDiscoveryScript('setup-scheduled-scan.sh', args);
|
|
284
|
+
await runDiscoverySchedule({ apiKey: opts.apiKey, domain: opts.domain });
|
|
272
285
|
} catch (err) {
|
|
273
286
|
output.error(err.message);
|
|
274
287
|
process.exitCode = 1;
|
|
@@ -349,4 +362,4 @@ Examples:
|
|
|
349
362
|
});
|
|
350
363
|
}
|
|
351
364
|
|
|
352
|
-
module.exports = { register, runDiscoveryScan, classifyDiscoveryExit };
|
|
365
|
+
module.exports = { register, runDiscoveryScan, runDiscoverySchedule, classifyDiscoveryExit };
|
package/src/commands/onboard.js
CHANGED
|
@@ -3,7 +3,7 @@ const config = require('../config');
|
|
|
3
3
|
const output = require('../output');
|
|
4
4
|
const { ensureLoggedIn } = require('../auth');
|
|
5
5
|
const { runSetupAllBundle, runMdmSetupAllBundle, checkRoot, ALL_TOOLS, MDM_ALL_TOOLS } = require('./setup');
|
|
6
|
-
const { runDiscoveryScan } = require('./discover');
|
|
6
|
+
const { runDiscoveryScan, runDiscoverySchedule } = require('./discover');
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Builds the recovery-command suffix for partial-failure hints.
|
|
@@ -26,7 +26,8 @@ function register(program) {
|
|
|
26
26
|
.requiredOption('--api-key <key>', 'User API key (for tool setup and login)')
|
|
27
27
|
.requiredOption('--discovery-key <key>', 'Discovery API key (for device scan)')
|
|
28
28
|
.option('--domain <url>', 'Backend URL for discovery (defaults to configured backend)')
|
|
29
|
-
.option('--backfill', 'Seed historical Claude Code / Codex sessions from local transcripts into Unbound analytics (Cursor skipped automatically)')
|
|
29
|
+
.option('--backfill', 'Seed historical Claude Code / Codex / Copilot sessions from local transcripts into Unbound analytics (Cursor skipped automatically)')
|
|
30
|
+
.option('--cron', 'Set up a recurring 12-hour discovery scan instead of a one-time scan (macOS only)')
|
|
30
31
|
.addOption(new Option('--backend-url <url>', 'Override backend URL for setup scripts (dev only)').hideHelp())
|
|
31
32
|
.addOption(new Option('--frontend-url <url>', 'Override frontend URL for setup scripts (dev only)').hideHelp())
|
|
32
33
|
.addOption(new Option('--gateway-url <url>', 'Override gateway URL for setup scripts (dev only)').hideHelp())
|
|
@@ -34,7 +35,8 @@ function register(program) {
|
|
|
34
35
|
Runs the full onboarding flow for an end user:
|
|
35
36
|
1. Logs in with --api-key and stores credentials.
|
|
36
37
|
2. Installs the default tool bundle: ${ALL_TOOLS.join(', ')}.
|
|
37
|
-
3. Runs device discovery with --discovery-key.
|
|
38
|
+
3. Runs device discovery with --discovery-key. With --cron, sets up a
|
|
39
|
+
recurring 12-hour discovery scan (macOS only) instead of a one-time scan.
|
|
38
40
|
|
|
39
41
|
The user API key and discovery API key are separate keys obtained from
|
|
40
42
|
different parts of the Unbound dashboard. Discovery uses its own key
|
|
@@ -48,6 +50,7 @@ For admin device enrollment via MDM, use \`unbound onboard-mdm\` instead.
|
|
|
48
50
|
Examples:
|
|
49
51
|
$ unbound onboard --api-key <USER_KEY> --discovery-key <DISCOVERY_KEY>
|
|
50
52
|
$ unbound onboard --api-key <USER_KEY> --discovery-key <DISCOVERY_KEY> --backfill
|
|
53
|
+
$ unbound onboard --api-key <USER_KEY> --discovery-key <DISCOVERY_KEY> --cron
|
|
51
54
|
$ sudo unbound onboard --api-key <USER_KEY> --discovery-key <DISCOVERY_KEY>
|
|
52
55
|
`)
|
|
53
56
|
.action(async (opts) => {
|
|
@@ -90,7 +93,13 @@ Examples:
|
|
|
90
93
|
console.log('');
|
|
91
94
|
output.info('Step 2/2: Running device discovery');
|
|
92
95
|
console.log('');
|
|
93
|
-
|
|
96
|
+
if (opts.cron && process.platform === 'darwin') {
|
|
97
|
+
// --cron sets up the recurring 12-hour scan, which also scans now.
|
|
98
|
+
await runDiscoverySchedule({ apiKey: opts.discoveryKey, domain: discoveryDomain });
|
|
99
|
+
} else {
|
|
100
|
+
if (opts.cron) output.warn('--cron is macOS-only; running a one-time scan instead.');
|
|
101
|
+
await runDiscoveryScan({ apiKey: opts.discoveryKey, domain: discoveryDomain });
|
|
102
|
+
}
|
|
94
103
|
|
|
95
104
|
console.log('');
|
|
96
105
|
output.success('Onboarding complete');
|
|
@@ -98,8 +107,13 @@ Examples:
|
|
|
98
107
|
if (!err.displayed) output.error(err.message);
|
|
99
108
|
if (setupSucceeded) {
|
|
100
109
|
const suffix = domainHintSuffix(discoveryDomain);
|
|
110
|
+
// Point at the path the user actually wanted: the scheduler when
|
|
111
|
+
// --cron was used on macOS, otherwise the one-time scan.
|
|
112
|
+
const retryCmd = opts.cron && process.platform === 'darwin'
|
|
113
|
+
? `unbound discover schedule --api-key <DISCOVERY_KEY>${suffix}`
|
|
114
|
+
: `unbound discover --api-key <DISCOVERY_KEY>${suffix}`;
|
|
101
115
|
console.error(' Tool setup completed successfully — only discovery failed.');
|
|
102
|
-
console.error(` Re-run discovery only with:
|
|
116
|
+
console.error(` Re-run discovery only with: ${retryCmd}`);
|
|
103
117
|
}
|
|
104
118
|
process.exitCode = 1;
|
|
105
119
|
}
|
|
@@ -116,7 +130,7 @@ Examples:
|
|
|
116
130
|
.requiredOption('--admin-api-key <key>', 'Admin API key for MDM enrollment')
|
|
117
131
|
.requiredOption('--discovery-key <key>', 'Discovery API key (for device scan)')
|
|
118
132
|
.option('--domain <url>', 'Backend URL for discovery (defaults to configured backend)')
|
|
119
|
-
.option('--backfill', 'Seed historical Claude Code / Codex sessions from local transcripts into Unbound analytics (Cursor skipped automatically)')
|
|
133
|
+
.option('--backfill', 'Seed historical Claude Code / Codex / Copilot sessions from local transcripts into Unbound analytics (Cursor skipped automatically)')
|
|
120
134
|
.addOption(new Option('--backend-url <url>', 'Override backend URL for setup scripts (dev only)').hideHelp())
|
|
121
135
|
.addOption(new Option('--frontend-url <url>', 'Override frontend URL for setup scripts (dev only)').hideHelp())
|
|
122
136
|
.addOption(new Option('--gateway-url <url>', 'Override gateway URL for setup scripts (dev only)').hideHelp())
|
package/src/commands/setup.js
CHANGED
|
@@ -232,11 +232,13 @@ function buildScriptArgs(apiKey, { backendUrl, frontendUrl, gatewayUrl, clear, m
|
|
|
232
232
|
return args.trim();
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
-
// Backfill only applies to the hooks variants of Claude Code / Codex;
|
|
236
|
-
// mode and Cursor have no local transcripts to seed.
|
|
235
|
+
// Backfill only applies to the hooks variants of Claude Code / Codex / Copilot;
|
|
236
|
+
// gateway mode and Cursor have no local transcripts to seed.
|
|
237
237
|
function scriptSupportsBackfill(scriptPath) {
|
|
238
238
|
return scriptPath.includes('/hooks/') && (
|
|
239
|
-
scriptPath.startsWith('claude-code/') ||
|
|
239
|
+
scriptPath.startsWith('claude-code/') ||
|
|
240
|
+
scriptPath.startsWith('codex/') ||
|
|
241
|
+
scriptPath.startsWith('copilot/')
|
|
240
242
|
);
|
|
241
243
|
}
|
|
242
244
|
|
|
@@ -352,7 +354,7 @@ function register(program) {
|
|
|
352
354
|
.option('--subscription', 'Use subscription mode for Claude Code / Codex (hooks only)')
|
|
353
355
|
.option('--gateway', 'Use gateway mode for Claude Code / Codex (Unbound as AI provider)')
|
|
354
356
|
.option('--all', 'Set up the default bundle: Cursor, Copilot, Claude Code (hooks), Codex (hooks)')
|
|
355
|
-
.option('--backfill', 'Seed historical Claude Code / Codex sessions from local transcripts into Unbound analytics (subscription/hooks mode only; Cursor
|
|
357
|
+
.option('--backfill', 'Seed historical Claude Code / Codex / Copilot sessions from local transcripts into Unbound analytics (subscription/hooks mode only; Cursor unsupported)')
|
|
356
358
|
.addOption(new Option('--backend-url <url>', 'Override backend URL for setup scripts (dev only)').hideHelp())
|
|
357
359
|
.addOption(new Option('--frontend-url <url>', 'Override frontend URL for setup scripts (dev only)').hideHelp())
|
|
358
360
|
.addOption(new Option('--gateway-url <url>', 'Override gateway URL for setup scripts (dev only)').hideHelp())
|
|
@@ -386,9 +388,10 @@ Examples:
|
|
|
386
388
|
$ unbound setup --all Set up the default bundle
|
|
387
389
|
$ unbound setup --all --api-key <key> Login + set up the bundle
|
|
388
390
|
|
|
389
|
-
Seed historical sessions (Claude Code / Codex subscription mode
|
|
391
|
+
Seed historical sessions (Claude Code / Codex subscription mode + Copilot):
|
|
390
392
|
$ unbound setup claude-code --subscription --backfill Install hooks AND backfill local history
|
|
391
393
|
$ unbound setup codex --subscription --backfill Install hooks AND backfill local history
|
|
394
|
+
$ unbound setup copilot --backfill Install hooks AND backfill local history
|
|
392
395
|
|
|
393
396
|
One-step login and setup:
|
|
394
397
|
$ unbound setup cursor --api-key <key> Login + set up Cursor
|
|
@@ -648,7 +651,7 @@ requires authentication.
|
|
|
648
651
|
.option('--admin-api-key <key>', 'Admin API key for MDM enrollment (not required with --clear)')
|
|
649
652
|
.option('--clear', 'Remove Unbound configuration for the specified tools (no API key required)')
|
|
650
653
|
.option('--all', 'Set up all available tools')
|
|
651
|
-
.option('--backfill', 'Seed historical Claude Code / Codex sessions from local transcripts into Unbound analytics (subscription/hooks mode only; Cursor
|
|
654
|
+
.option('--backfill', 'Seed historical Claude Code / Codex / Copilot sessions from local transcripts into Unbound analytics (subscription/hooks mode only; Cursor unsupported)')
|
|
652
655
|
.addHelpText('after', `
|
|
653
656
|
Available tools:
|
|
654
657
|
cursor Cursor IDE
|
|
@@ -671,6 +674,8 @@ Setup examples (require --admin-api-key):
|
|
|
671
674
|
$ sudo unbound setup mdm --admin-api-key KEY --all
|
|
672
675
|
$ sudo unbound setup mdm --admin-api-key KEY claude-code-subscription --backfill
|
|
673
676
|
Install hooks AND backfill local history
|
|
677
|
+
$ sudo unbound setup mdm --admin-api-key KEY copilot --backfill
|
|
678
|
+
Install Copilot hooks AND backfill local history
|
|
674
679
|
|
|
675
680
|
Clear examples (no API key required):
|
|
676
681
|
$ sudo unbound setup mdm --clear cursor
|
|
@@ -802,9 +807,9 @@ async function runSetupAllBundle(apiKey, { backendUrl, frontendUrl, gatewayUrl,
|
|
|
802
807
|
}
|
|
803
808
|
}
|
|
804
809
|
// Build args per-tool so --backfill only goes to tools whose script
|
|
805
|
-
// actually supports it (Claude Code hooks
|
|
806
|
-
// print "not supported"; passing the flag to gateway-mode
|
|
807
|
-
// error out — `scriptSupportsBackfill` checks for both.
|
|
810
|
+
// actually supports it (Claude Code hooks, Codex hooks, Copilot hooks).
|
|
811
|
+
// Cursor would print "not supported"; passing the flag to gateway-mode
|
|
812
|
+
// scripts would error out — `scriptSupportsBackfill` checks for both.
|
|
808
813
|
return runBatch(resolvedTools, (tool) => {
|
|
809
814
|
const args = buildScriptArgs(apiKey, {
|
|
810
815
|
backendUrl, frontendUrl, gatewayUrl,
|
|
@@ -843,4 +848,5 @@ module.exports = {
|
|
|
843
848
|
ALL_TOOLS,
|
|
844
849
|
MDM_ALL_TOOLS,
|
|
845
850
|
buildScriptArgs,
|
|
851
|
+
scriptSupportsBackfill,
|
|
846
852
|
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
const { test } = require('node:test');
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
const discover = require('../src/commands/discover');
|
|
7
|
+
|
|
8
|
+
const DISCOVER_SRC_PATH = path.join(__dirname, '..', 'src', 'commands', 'discover.js');
|
|
9
|
+
const ONBOARD_SRC_PATH = path.join(__dirname, '..', 'src', 'commands', 'onboard.js');
|
|
10
|
+
|
|
11
|
+
const discoverSrc = fs.readFileSync(DISCOVER_SRC_PATH, 'utf8');
|
|
12
|
+
const onboardSrc = fs.readFileSync(ONBOARD_SRC_PATH, 'utf8');
|
|
13
|
+
|
|
14
|
+
// Extracts the textual body of a top-level `async function <name>(...) { ... }`
|
|
15
|
+
// by brace-matching from the function keyword. Lets us assert on a single
|
|
16
|
+
// function's body without false positives from the rest of the file.
|
|
17
|
+
function extractFunctionBody(src, name) {
|
|
18
|
+
const start = src.indexOf(`async function ${name}`);
|
|
19
|
+
assert.notEqual(start, -1, `expected "async function ${name}" in source`);
|
|
20
|
+
// Skip the parameter list (which may itself contain destructuring braces)
|
|
21
|
+
// by finding the matching close-paren first, then the body's opening brace.
|
|
22
|
+
const paramOpen = src.indexOf('(', start);
|
|
23
|
+
let pDepth = 0;
|
|
24
|
+
let paramClose = -1;
|
|
25
|
+
for (let i = paramOpen; i < src.length; i++) {
|
|
26
|
+
if (src[i] === '(') pDepth++;
|
|
27
|
+
else if (src[i] === ')') {
|
|
28
|
+
pDepth--;
|
|
29
|
+
if (pDepth === 0) { paramClose = i; break; }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
assert.notEqual(paramClose, -1, `unbalanced parens in ${name} signature`);
|
|
33
|
+
const open = src.indexOf('{', paramClose);
|
|
34
|
+
assert.notEqual(open, -1, `expected an opening brace after ${name}`);
|
|
35
|
+
let depth = 0;
|
|
36
|
+
for (let i = open; i < src.length; i++) {
|
|
37
|
+
if (src[i] === '{') depth++;
|
|
38
|
+
else if (src[i] === '}') {
|
|
39
|
+
depth--;
|
|
40
|
+
if (depth === 0) return src.slice(open, i + 1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
throw new Error(`unbalanced braces while extracting ${name}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const scheduleBody = extractFunctionBody(discoverSrc, 'runDiscoverySchedule');
|
|
47
|
+
|
|
48
|
+
test('runDiscoverySchedule is exported as a function', () => {
|
|
49
|
+
assert.equal(typeof discover.runDiscoverySchedule, 'function');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('runDiscoverySchedule takes a single destructured options object', () => {
|
|
53
|
+
// One formal parameter: ({ apiKey, domain }). Arity counts that single object.
|
|
54
|
+
assert.equal(discover.runDiscoverySchedule.length, 1);
|
|
55
|
+
// Accepts exactly apiKey and domain — no third field could carry backfill.
|
|
56
|
+
assert.match(discoverSrc, /async function runDiscoverySchedule\(\{\s*apiKey,\s*domain\s*\}\)/);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('the scheduled path runs setup-scheduled-scan.sh, never install.sh', () => {
|
|
60
|
+
assert.ok(
|
|
61
|
+
scheduleBody.includes("'setup-scheduled-scan.sh'"),
|
|
62
|
+
'runDiscoverySchedule must schedule setup-scheduled-scan.sh'
|
|
63
|
+
);
|
|
64
|
+
assert.ok(
|
|
65
|
+
!scheduleBody.includes('install.sh'),
|
|
66
|
+
'runDiscoverySchedule must not invoke install.sh'
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Core WEB-4499 regression guard: backfill is a one-time setup operation and
|
|
71
|
+
// must be structurally impossible to reach the recurring scheduled scan.
|
|
72
|
+
test('runDiscoverySchedule body contains no backfill reference', () => {
|
|
73
|
+
assert.ok(
|
|
74
|
+
!scheduleBody.toLowerCase().includes('backfill'),
|
|
75
|
+
'backfill must never appear in the scheduled-scan code path'
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('runDiscoverySchedule shell-escapes both apiKey and domain', () => {
|
|
80
|
+
// Both user-influenced values pass through shellEscape before hitting the
|
|
81
|
+
// shell:true spawn, so neither can break out of the command string.
|
|
82
|
+
assert.match(scheduleBody, /--api-key \$\{shellEscape\(apiKey\)\}/);
|
|
83
|
+
assert.match(scheduleBody, /--domain \$\{shellEscape\(resolvedDomain\)\}/);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// The onboard --cron path must call the shared schedule helper with only
|
|
87
|
+
// apiKey + domain — never forwarding opts.backfill into the schedule.
|
|
88
|
+
test('onboard schedules via runDiscoverySchedule with only apiKey and domain', () => {
|
|
89
|
+
assert.ok(
|
|
90
|
+
onboardSrc.includes('runDiscoverySchedule({ apiKey: opts.discoveryKey, domain: discoveryDomain })'),
|
|
91
|
+
'onboard must call runDiscoverySchedule with exactly { apiKey, domain }'
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('onboard never passes backfill into runDiscoverySchedule', () => {
|
|
96
|
+
// Find the runDiscoverySchedule call site in onboard and confirm its
|
|
97
|
+
// argument object carries no backfill key.
|
|
98
|
+
const callIdx = onboardSrc.indexOf('runDiscoverySchedule(');
|
|
99
|
+
assert.notEqual(callIdx, -1, 'expected a runDiscoverySchedule call in onboard');
|
|
100
|
+
const open = onboardSrc.indexOf('(', callIdx);
|
|
101
|
+
const close = onboardSrc.indexOf(')', open);
|
|
102
|
+
const callArgs = onboardSrc.slice(open, close + 1);
|
|
103
|
+
assert.ok(!callArgs.toLowerCase().includes('backfill'), callArgs);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('runDiscoverySchedule is re-exported into onboard from discover', () => {
|
|
107
|
+
// onboard pulls the helper from ./discover rather than re-implementing it,
|
|
108
|
+
// so both the `discover schedule` command and `onboard --cron` share one path.
|
|
109
|
+
assert.match(
|
|
110
|
+
onboardSrc,
|
|
111
|
+
/const \{[^}]*runDiscoverySchedule[^}]*\} = require\('\.\/discover'\)/
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('runDiscoverySchedule rejects when apiKey is missing', async () => {
|
|
116
|
+
// Mirrors runDiscoveryScan's guard so a future caller can't silently send
|
|
117
|
+
// --api-key 'undefined' to the schedule script. Throws before any network.
|
|
118
|
+
await assert.rejects(
|
|
119
|
+
() => discover.runDiscoverySchedule({ domain: 'https://example.com' }),
|
|
120
|
+
/Discovery API key is required/
|
|
121
|
+
);
|
|
122
|
+
});
|
package/test/setup-args.test.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { test } = require('node:test');
|
|
2
2
|
const assert = require('node:assert/strict');
|
|
3
|
-
const { buildScriptArgs } = require('../src/commands/setup');
|
|
3
|
+
const { buildScriptArgs, scriptSupportsBackfill } = require('../src/commands/setup');
|
|
4
4
|
|
|
5
5
|
// shellEscape single-quotes every value, so a real key surfaces as
|
|
6
6
|
// --api-key '<key>' at the head of the argv tail.
|
|
@@ -51,6 +51,25 @@ test('buildScriptArgs: backfill:true appends --backfill', () => {
|
|
|
51
51
|
assert.ok(args.includes('--backfill'), args);
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
+
// Backfill is supported only by the hooks variants of Claude Code, Codex, and
|
|
55
|
+
// Copilot. Gateway-mode scripts and Cursor have no local transcripts to seed.
|
|
56
|
+
test('scriptSupportsBackfill: hooks variants of claude-code, codex, copilot are supported', () => {
|
|
57
|
+
assert.ok(scriptSupportsBackfill('claude-code/hooks/setup.py'));
|
|
58
|
+
assert.ok(scriptSupportsBackfill('claude-code/hooks/mdm/setup.py'));
|
|
59
|
+
assert.ok(scriptSupportsBackfill('codex/hooks/setup.py'));
|
|
60
|
+
assert.ok(scriptSupportsBackfill('codex/hooks/mdm/setup.py'));
|
|
61
|
+
assert.ok(scriptSupportsBackfill('copilot/hooks/setup.py'));
|
|
62
|
+
assert.ok(scriptSupportsBackfill('copilot/hooks/mdm/setup.py'));
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('scriptSupportsBackfill: cursor and gateway-mode scripts are unsupported', () => {
|
|
66
|
+
assert.ok(!scriptSupportsBackfill('cursor/setup.py'));
|
|
67
|
+
assert.ok(!scriptSupportsBackfill('cursor/mdm/setup.py'));
|
|
68
|
+
assert.ok(!scriptSupportsBackfill('claude-code/gateway/setup.py'));
|
|
69
|
+
assert.ok(!scriptSupportsBackfill('codex/gateway/setup.py'));
|
|
70
|
+
assert.ok(!scriptSupportsBackfill('gemini-cli/gateway/setup.py'));
|
|
71
|
+
});
|
|
72
|
+
|
|
54
73
|
// MDM scripts have no browser-auth flow, so --domain (frontend URL) is
|
|
55
74
|
// suppressed even when a frontendUrl is supplied.
|
|
56
75
|
test('buildScriptArgs: mdm:true suppresses --domain even with frontendUrl', () => {
|