@distributionos/cli 0.1.14 → 0.1.16
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 +7 -7
- package/package.json +1 -1
- package/src/api.js +125 -125
- package/src/bootstrap.js +15 -6
- package/src/cli.js +216 -213
- package/src/constants.js +8 -8
- package/src/initialization.js +63 -63
- package/src/mcp-config.js +319 -187
- package/src/mutate.js +46 -43
- package/src/oauth.js +30 -30
- package/src/plan.js +175 -169
- package/src/setup-report.js +247 -216
- package/src/validation.js +108 -0
package/src/cli.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { CLI_LOCAL_VERSION, DEFAULT_API_BASE } from './constants.js';
|
|
2
|
-
import {
|
|
3
|
-
ensureAnalyticsSite,
|
|
4
|
-
reportSetupInstallation,
|
|
5
|
-
reportImplementation,
|
|
6
|
-
resolveAuthToken,
|
|
7
|
-
verifyAnalyticsInstall,
|
|
8
|
-
} from './api.js';
|
|
1
|
+
import { CLI_LOCAL_VERSION, DEFAULT_API_BASE } from './constants.js';
|
|
2
|
+
import {
|
|
3
|
+
ensureAnalyticsSite,
|
|
4
|
+
reportSetupInstallation,
|
|
5
|
+
reportImplementation,
|
|
6
|
+
resolveAuthToken,
|
|
7
|
+
verifyAnalyticsInstall,
|
|
8
|
+
} from './api.js';
|
|
9
9
|
import { loginWithOAuth } from './oauth.js';
|
|
10
10
|
import { createSetupPlan, formatPlanText } from './plan.js';
|
|
11
11
|
import { applySetupPlan } from './mutate.js';
|
|
12
|
-
import { submitInitialization } from './initialization.js';
|
|
13
|
-
import { compareValidationResults, inspectInstallArtifacts, runValidationPlan } from './validation.js';
|
|
14
|
-
import { buildSetupReport } from './setup-report.js';
|
|
12
|
+
import { submitInitialization } from './initialization.js';
|
|
13
|
+
import { compareValidationResults, inspectInstallArtifacts, runValidationPlan } from './validation.js';
|
|
14
|
+
import { buildSetupReport } from './setup-report.js';
|
|
15
15
|
|
|
16
16
|
export async function runCli(argv, runtime) {
|
|
17
17
|
const stdout = runtime.stdout;
|
|
@@ -141,21 +141,21 @@ export async function runCli(argv, runtime) {
|
|
|
141
141
|
if (!authReady) return 1;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
let plan = await createSetupPlan({
|
|
145
|
-
appId: args.appId,
|
|
146
|
-
cwd: args.cwd ?? runtime.cwd,
|
|
147
|
-
apiBase: args.apiBase ?? DEFAULT_API_BASE,
|
|
144
|
+
let plan = await createSetupPlan({
|
|
145
|
+
appId: args.appId,
|
|
146
|
+
cwd: args.cwd ?? runtime.cwd,
|
|
147
|
+
apiBase: args.apiBase ?? DEFAULT_API_BASE,
|
|
148
148
|
env: runtime.env,
|
|
149
149
|
fetch: runtime.fetch,
|
|
150
|
-
noFetch: args.noFetch,
|
|
151
|
-
apply: args.apply,
|
|
152
|
-
});
|
|
153
|
-
plan.setupReport = await submitSetupReportSafely({
|
|
154
|
-
args,
|
|
155
|
-
runtime,
|
|
156
|
-
plan,
|
|
157
|
-
phase: 'plan',
|
|
158
|
-
});
|
|
150
|
+
noFetch: args.noFetch,
|
|
151
|
+
apply: args.apply,
|
|
152
|
+
});
|
|
153
|
+
plan.setupReport = await submitSetupReportSafely({
|
|
154
|
+
args,
|
|
155
|
+
runtime,
|
|
156
|
+
plan,
|
|
157
|
+
phase: 'plan',
|
|
158
|
+
});
|
|
159
159
|
|
|
160
160
|
if (!args.apply && !args.yes) {
|
|
161
161
|
stdout.write(args.json ? `${JSON.stringify(plan, null, 2)}\n` : formatPlanText(plan));
|
|
@@ -165,19 +165,19 @@ export async function runCli(argv, runtime) {
|
|
|
165
165
|
args.apply = true;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
if (args.yes) {
|
|
169
|
-
args.apply = true;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (args.apply && hasRemoteAccessBlock(plan)) {
|
|
173
|
-
stderr.write(formatRemoteAccessBlock(plan));
|
|
174
|
-
return 1;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (args.apply && plan.repo.git.dirty && !args.allowDirty) {
|
|
178
|
-
stderr.write('Refusing to apply changes to a dirty worktree. Commit/stash changes or pass --allow-dirty.\n');
|
|
179
|
-
return 1;
|
|
180
|
-
}
|
|
168
|
+
if (args.yes) {
|
|
169
|
+
args.apply = true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (args.apply && hasRemoteAccessBlock(plan)) {
|
|
173
|
+
stderr.write(formatRemoteAccessBlock(plan));
|
|
174
|
+
return 1;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (args.apply && plan.repo.git.dirty && !args.allowDirty) {
|
|
178
|
+
stderr.write('Refusing to apply changes to a dirty worktree. Commit/stash changes or pass --allow-dirty.\n');
|
|
179
|
+
return 1;
|
|
180
|
+
}
|
|
181
181
|
|
|
182
182
|
if (args.apply && !args.skipAnalytics) {
|
|
183
183
|
const ensured = await ensureAnalyticsSite({
|
|
@@ -193,21 +193,21 @@ export async function runCli(argv, runtime) {
|
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
plan = await createSetupPlan({
|
|
197
|
-
appId: args.appId,
|
|
198
|
-
cwd: args.cwd ?? runtime.cwd,
|
|
199
|
-
apiBase: args.apiBase ?? DEFAULT_API_BASE,
|
|
196
|
+
plan = await createSetupPlan({
|
|
197
|
+
appId: args.appId,
|
|
198
|
+
cwd: args.cwd ?? runtime.cwd,
|
|
199
|
+
apiBase: args.apiBase ?? DEFAULT_API_BASE,
|
|
200
200
|
env: runtime.env,
|
|
201
201
|
fetch: runtime.fetch,
|
|
202
|
-
noFetch: args.noFetch,
|
|
203
|
-
apply: args.apply,
|
|
204
|
-
});
|
|
205
|
-
plan.setupReport = await submitSetupReportSafely({
|
|
206
|
-
args,
|
|
207
|
-
runtime,
|
|
208
|
-
plan,
|
|
209
|
-
phase: args.apply ? 'apply_started' : 'plan',
|
|
210
|
-
});
|
|
202
|
+
noFetch: args.noFetch,
|
|
203
|
+
apply: args.apply,
|
|
204
|
+
});
|
|
205
|
+
plan.setupReport = await submitSetupReportSafely({
|
|
206
|
+
args,
|
|
207
|
+
runtime,
|
|
208
|
+
plan,
|
|
209
|
+
phase: args.apply ? 'apply_started' : 'plan',
|
|
210
|
+
});
|
|
211
211
|
|
|
212
212
|
if (!args.apply) {
|
|
213
213
|
stdout.write(args.json ? `${JSON.stringify(plan, null, 2)}\n` : formatPlanText(plan));
|
|
@@ -219,119 +219,119 @@ export async function runCli(argv, runtime) {
|
|
|
219
219
|
const changes = await applySetupPlan(plan, {
|
|
220
220
|
allowDirty: args.allowDirty,
|
|
221
221
|
skipAnalytics: args.skipAnalytics,
|
|
222
|
-
});
|
|
223
|
-
const postValidation = args.skipValidate ? [] : await runValidationPlan(plan);
|
|
224
|
-
const validation = compareValidationResults(baselineValidation, postValidation);
|
|
225
|
-
const inspection = args.skipAnalytics ? [] : await inspectInstallArtifacts(plan);
|
|
226
|
-
const completedPlan = await createSetupPlan({
|
|
227
|
-
appId: args.appId,
|
|
228
|
-
cwd: args.cwd ?? runtime.cwd,
|
|
229
|
-
apiBase: args.apiBase ?? DEFAULT_API_BASE,
|
|
230
|
-
env: runtime.env,
|
|
231
|
-
fetch: runtime.fetch,
|
|
232
|
-
noFetch: args.noFetch,
|
|
233
|
-
apply: true,
|
|
234
|
-
});
|
|
235
|
-
completedPlan.setupReport = await submitSetupReportSafely({
|
|
236
|
-
args,
|
|
237
|
-
runtime,
|
|
238
|
-
plan: completedPlan,
|
|
239
|
-
phase: 'apply_completed',
|
|
240
|
-
details: {
|
|
241
|
-
changes,
|
|
222
|
+
});
|
|
223
|
+
const postValidation = args.skipValidate ? [] : await runValidationPlan(plan);
|
|
224
|
+
const validation = compareValidationResults(baselineValidation, postValidation);
|
|
225
|
+
const inspection = args.skipAnalytics ? [] : await inspectInstallArtifacts(plan);
|
|
226
|
+
const completedPlan = await createSetupPlan({
|
|
227
|
+
appId: args.appId,
|
|
228
|
+
cwd: args.cwd ?? runtime.cwd,
|
|
229
|
+
apiBase: args.apiBase ?? DEFAULT_API_BASE,
|
|
230
|
+
env: runtime.env,
|
|
231
|
+
fetch: runtime.fetch,
|
|
232
|
+
noFetch: args.noFetch,
|
|
233
|
+
apply: true,
|
|
234
|
+
});
|
|
235
|
+
completedPlan.setupReport = await submitSetupReportSafely({
|
|
236
|
+
args,
|
|
237
|
+
runtime,
|
|
238
|
+
plan: completedPlan,
|
|
239
|
+
phase: 'apply_completed',
|
|
240
|
+
details: {
|
|
241
|
+
changes,
|
|
242
242
|
validation,
|
|
243
243
|
inspection,
|
|
244
|
-
requiresAgentRestart: changes.some((change) => change.type === 'mcp' && change.changed),
|
|
244
|
+
requiresAgentRestart: changes.some((change) => (change.type === 'mcp' || change.type === 'codex-mcp') && change.changed),
|
|
245
245
|
},
|
|
246
246
|
});
|
|
247
|
-
const token = await resolveAuthToken({
|
|
248
|
-
env: runtime.env,
|
|
249
|
-
apiBase: args.apiBase ?? DEFAULT_API_BASE,
|
|
250
|
-
appId: args.appId,
|
|
251
|
-
fetchImpl: runtime.fetch,
|
|
247
|
+
const token = await resolveAuthToken({
|
|
248
|
+
env: runtime.env,
|
|
249
|
+
apiBase: args.apiBase ?? DEFAULT_API_BASE,
|
|
250
|
+
appId: args.appId,
|
|
251
|
+
fetchImpl: runtime.fetch,
|
|
252
252
|
});
|
|
253
|
-
const initialization = token
|
|
254
|
-
? await submitInitialization({
|
|
255
|
-
plan: completedPlan,
|
|
256
|
-
token: token.value,
|
|
257
|
-
apiBase: args.apiBase ?? DEFAULT_API_BASE,
|
|
258
|
-
fetchImpl: runtime.fetch,
|
|
259
|
-
domain: args.domain,
|
|
260
|
-
})
|
|
261
|
-
: null;
|
|
262
|
-
|
|
263
|
-
stdout.write(args.json
|
|
264
|
-
? `${JSON.stringify({ plan: completedPlan, changes, initialization, validation, inspection }, null, 2)}\n`
|
|
265
|
-
: formatApplyResult(completedPlan, changes, initialization, validation, inspection, args.skipValidate, args.apiBase ?? DEFAULT_API_BASE));
|
|
253
|
+
const initialization = token
|
|
254
|
+
? await submitInitialization({
|
|
255
|
+
plan: completedPlan,
|
|
256
|
+
token: token.value,
|
|
257
|
+
apiBase: args.apiBase ?? DEFAULT_API_BASE,
|
|
258
|
+
fetchImpl: runtime.fetch,
|
|
259
|
+
domain: args.domain,
|
|
260
|
+
})
|
|
261
|
+
: null;
|
|
262
|
+
|
|
263
|
+
stdout.write(args.json
|
|
264
|
+
? `${JSON.stringify({ plan: completedPlan, changes, initialization, validation, inspection }, null, 2)}\n`
|
|
265
|
+
: formatApplyResult(completedPlan, changes, initialization, validation, inspection, args.skipValidate, args.apiBase ?? DEFAULT_API_BASE));
|
|
266
266
|
return hasBlockingApplyFailure(validation, inspection) ? 1 : 0;
|
|
267
|
-
} catch (error) {
|
|
268
|
-
await submitSetupReportSafely({
|
|
269
|
-
args,
|
|
270
|
-
runtime,
|
|
271
|
-
plan,
|
|
272
|
-
phase: 'apply_failed',
|
|
273
|
-
details: {
|
|
274
|
-
applyResult: {
|
|
275
|
-
validation: [{
|
|
276
|
-
name: 'apply',
|
|
277
|
-
status: 'failed',
|
|
278
|
-
}],
|
|
279
|
-
},
|
|
280
|
-
},
|
|
281
|
-
});
|
|
282
|
-
stderr.write(`${error instanceof Error ? error.message : 'Setup apply failed'}\n`);
|
|
283
|
-
return 1;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
async function submitSetupReportSafely({ args, runtime, plan, phase, details = {} }) {
|
|
288
|
-
if (args.noFetch || args.skipSetupReport) {
|
|
289
|
-
return null;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
try {
|
|
293
|
-
const report = await buildSetupReport(plan, phase, details);
|
|
294
|
-
const result = await reportSetupInstallation({
|
|
295
|
-
appId: args.appId,
|
|
296
|
-
apiBase: args.apiBase ?? DEFAULT_API_BASE,
|
|
297
|
-
env: runtime.env,
|
|
298
|
-
fetch: runtime.fetch,
|
|
299
|
-
report,
|
|
300
|
-
});
|
|
301
|
-
return {
|
|
302
|
-
status: 'submitted',
|
|
303
|
-
phase,
|
|
304
|
-
result,
|
|
305
|
-
};
|
|
306
|
-
} catch (error) {
|
|
307
|
-
return {
|
|
308
|
-
status: 'failed',
|
|
309
|
-
phase,
|
|
310
|
-
error: error instanceof Error ? error.message : 'Setup report failed',
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
function hasBlockingApplyFailure(validation, inspection) {
|
|
316
|
-
return validation.some((result) => result.introducedFailure) ||
|
|
317
|
-
inspection.some((result) => result.status === 'failed');
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function hasRemoteAccessBlock(plan) {
|
|
321
|
-
return plan.remote?.access?.reason === 'subscription_required';
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
function formatRemoteAccessBlock(plan) {
|
|
325
|
-
const billingUrl = plan.remote?.access?.billingUrl ?? `${plan.apiBase ?? DEFAULT_API_BASE}/dashboard/account/billing`;
|
|
326
|
-
return [
|
|
327
|
-
'DistributionOS setup requires an active Builder or Distributor subscription before applying changes.',
|
|
328
|
-
`Manage billing: ${billingUrl}`,
|
|
329
|
-
'No files were changed.',
|
|
330
|
-
'',
|
|
331
|
-
].join('\n');
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
export function parseArgs(rawArgs) {
|
|
267
|
+
} catch (error) {
|
|
268
|
+
await submitSetupReportSafely({
|
|
269
|
+
args,
|
|
270
|
+
runtime,
|
|
271
|
+
plan,
|
|
272
|
+
phase: 'apply_failed',
|
|
273
|
+
details: {
|
|
274
|
+
applyResult: {
|
|
275
|
+
validation: [{
|
|
276
|
+
name: 'apply',
|
|
277
|
+
status: 'failed',
|
|
278
|
+
}],
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
stderr.write(`${error instanceof Error ? error.message : 'Setup apply failed'}\n`);
|
|
283
|
+
return 1;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async function submitSetupReportSafely({ args, runtime, plan, phase, details = {} }) {
|
|
288
|
+
if (args.noFetch || args.skipSetupReport) {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
const report = await buildSetupReport(plan, phase, details);
|
|
294
|
+
const result = await reportSetupInstallation({
|
|
295
|
+
appId: args.appId,
|
|
296
|
+
apiBase: args.apiBase ?? DEFAULT_API_BASE,
|
|
297
|
+
env: runtime.env,
|
|
298
|
+
fetch: runtime.fetch,
|
|
299
|
+
report,
|
|
300
|
+
});
|
|
301
|
+
return {
|
|
302
|
+
status: 'submitted',
|
|
303
|
+
phase,
|
|
304
|
+
result,
|
|
305
|
+
};
|
|
306
|
+
} catch (error) {
|
|
307
|
+
return {
|
|
308
|
+
status: 'failed',
|
|
309
|
+
phase,
|
|
310
|
+
error: error instanceof Error ? error.message : 'Setup report failed',
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function hasBlockingApplyFailure(validation, inspection) {
|
|
316
|
+
return validation.some((result) => result.introducedFailure) ||
|
|
317
|
+
inspection.some((result) => result.status === 'failed');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function hasRemoteAccessBlock(plan) {
|
|
321
|
+
return plan.remote?.access?.reason === 'subscription_required';
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function formatRemoteAccessBlock(plan) {
|
|
325
|
+
const billingUrl = plan.remote?.access?.billingUrl ?? `${plan.apiBase ?? DEFAULT_API_BASE}/dashboard/account/billing`;
|
|
326
|
+
return [
|
|
327
|
+
'DistributionOS setup requires an active Builder or Distributor subscription before applying changes.',
|
|
328
|
+
`Manage billing: ${billingUrl}`,
|
|
329
|
+
'No files were changed.',
|
|
330
|
+
'',
|
|
331
|
+
].join('\n');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function parseArgs(rawArgs) {
|
|
335
335
|
const parsed = {
|
|
336
336
|
command: null,
|
|
337
337
|
appId: null,
|
|
@@ -343,11 +343,11 @@ export function parseArgs(rawArgs) {
|
|
|
343
343
|
apply: false,
|
|
344
344
|
allowDirty: false,
|
|
345
345
|
skipAnalytics: false,
|
|
346
|
-
skipValidate: false,
|
|
347
|
-
noOpen: false,
|
|
348
|
-
noLogin: false,
|
|
349
|
-
skipSetupReport: false,
|
|
350
|
-
yes: false,
|
|
346
|
+
skipValidate: false,
|
|
347
|
+
noOpen: false,
|
|
348
|
+
noLogin: false,
|
|
349
|
+
skipSetupReport: false,
|
|
350
|
+
yes: false,
|
|
351
351
|
url: null,
|
|
352
352
|
contentId: null,
|
|
353
353
|
contractVersion: null,
|
|
@@ -379,11 +379,11 @@ export function parseArgs(rawArgs) {
|
|
|
379
379
|
else if (arg === '--apply') parsed.apply = true;
|
|
380
380
|
else if (arg === '--allow-dirty') parsed.allowDirty = true;
|
|
381
381
|
else if (arg === '--skip-analytics') parsed.skipAnalytics = true;
|
|
382
|
-
else if (arg === '--skip-validate') parsed.skipValidate = true;
|
|
383
|
-
else if (arg === '--no-open') parsed.noOpen = true;
|
|
384
|
-
else if (arg === '--no-login') parsed.noLogin = true;
|
|
385
|
-
else if (arg === '--skip-setup-report') parsed.skipSetupReport = true;
|
|
386
|
-
else if (arg === '--yes' || arg === '-y') parsed.yes = true;
|
|
382
|
+
else if (arg === '--skip-validate') parsed.skipValidate = true;
|
|
383
|
+
else if (arg === '--no-open') parsed.noOpen = true;
|
|
384
|
+
else if (arg === '--no-login') parsed.noLogin = true;
|
|
385
|
+
else if (arg === '--skip-setup-report') parsed.skipSetupReport = true;
|
|
386
|
+
else if (arg === '--yes' || arg === '-y') parsed.yes = true;
|
|
387
387
|
else if (arg === '--app') parsed.appId = args[++index] ?? null;
|
|
388
388
|
else if (arg.startsWith('--app=')) parsed.appId = arg.slice('--app='.length);
|
|
389
389
|
else if (arg === '--cwd') parsed.cwd = args[++index] ?? null;
|
|
@@ -416,15 +416,15 @@ function helpText() {
|
|
|
416
416
|
return [
|
|
417
417
|
'DistributionOS CLI',
|
|
418
418
|
'',
|
|
419
|
-
'Usage:',
|
|
420
|
-
' distributionos setup --app <appId> [--api-base <url>] [--cwd <path>] [--json] [--no-fetch]',
|
|
421
|
-
' distributionos setup --app <appId> --apply [--domain <domain>] [--allow-dirty] [--skip-setup-report]',
|
|
419
|
+
'Usage:',
|
|
420
|
+
' distributionos setup --app <appId> [--api-base <url>] [--cwd <path>] [--json] [--no-fetch]',
|
|
421
|
+
' distributionos setup --app <appId> --apply [--domain <domain>] [--allow-dirty] [--skip-setup-report]',
|
|
422
422
|
' distributionos login --app <appId> [--api-base <url>]',
|
|
423
423
|
' distributionos verify --app <appId> --url <liveUrl> [--content-id <id>]',
|
|
424
424
|
' distributionos report-implementation --app <appId> --artifact <artifactId> [--url <liveUrl>] [--summary <text>]',
|
|
425
425
|
'',
|
|
426
|
-
'Default mode reviews the setup plan without changing files. Run again with --apply after the plan looks right.',
|
|
427
|
-
'If the repo has existing uncommitted changes, commit/stash first or use --allow-dirty when you recognize them.',
|
|
426
|
+
'Default mode reviews the setup plan without changing files. Run again with --apply after the plan looks right.',
|
|
427
|
+
'If the repo has existing uncommitted changes, commit/stash first or use --allow-dirty when you recognize them.',
|
|
428
428
|
'',
|
|
429
429
|
'Authentication:',
|
|
430
430
|
' Happy path uses DistributionOS MCP OAuth and stores app-scoped credentials outside the repo.',
|
|
@@ -528,13 +528,13 @@ function formatReportResult(result) {
|
|
|
528
528
|
return `${lines.join('\n')}\n`;
|
|
529
529
|
}
|
|
530
530
|
|
|
531
|
-
function formatApplyResult(plan, changes, initialization, validation, inspection, skipValidate, apiBase = DEFAULT_API_BASE) {
|
|
532
|
-
const reviewUrl = initialization?.reviewUrl
|
|
533
|
-
? absoluteDistributionOsUrl(initialization.reviewUrl, apiBase)
|
|
534
|
-
: null;
|
|
535
|
-
const lines = [
|
|
536
|
-
formatPlanText({ ...plan, mode: 'apply' }).trimEnd(),
|
|
537
|
-
'',
|
|
531
|
+
function formatApplyResult(plan, changes, initialization, validation, inspection, skipValidate, apiBase = DEFAULT_API_BASE) {
|
|
532
|
+
const reviewUrl = initialization?.reviewUrl
|
|
533
|
+
? absoluteDistributionOsUrl(initialization.reviewUrl, apiBase)
|
|
534
|
+
: null;
|
|
535
|
+
const lines = [
|
|
536
|
+
formatPlanText({ ...plan, mode: 'apply' }).trimEnd(),
|
|
537
|
+
'',
|
|
538
538
|
'Applied changes',
|
|
539
539
|
];
|
|
540
540
|
|
|
@@ -548,9 +548,9 @@ function formatApplyResult(plan, changes, initialization, validation, inspection
|
|
|
548
548
|
}
|
|
549
549
|
}
|
|
550
550
|
|
|
551
|
-
lines.push(reviewUrl
|
|
552
|
-
? `- Initialization submitted. Review URL: ${reviewUrl}`
|
|
553
|
-
: '- Initialization was not submitted because no auth token was available.');
|
|
551
|
+
lines.push(reviewUrl
|
|
552
|
+
? `- Initialization submitted. Review URL: ${reviewUrl}`
|
|
553
|
+
: '- Initialization was not submitted because no auth token was available.');
|
|
554
554
|
|
|
555
555
|
lines.push('', 'Validation');
|
|
556
556
|
if (skipValidate) {
|
|
@@ -579,36 +579,39 @@ function formatApplyResult(plan, changes, initialization, validation, inspection
|
|
|
579
579
|
for (const result of inspection) {
|
|
580
580
|
lines.push(`- ${result.name}: ${result.status} - ${result.message}`);
|
|
581
581
|
}
|
|
582
|
-
}
|
|
583
|
-
lines.push('- Nothing was committed, pushed, or deployed.');
|
|
584
|
-
lines.push('', 'Next step');
|
|
585
|
-
if (reviewUrl) {
|
|
586
|
-
lines.push(
|
|
582
|
+
}
|
|
583
|
+
lines.push('- Nothing was committed, pushed, or deployed.');
|
|
584
|
+
lines.push('', 'Next step');
|
|
585
|
+
if (reviewUrl) {
|
|
586
|
+
lines.push(
|
|
587
587
|
'- Open this DistributionOS review page now:',
|
|
588
588
|
` ${reviewUrl}`,
|
|
589
|
-
'-
|
|
590
|
-
'-
|
|
591
|
-
'-
|
|
592
|
-
'-
|
|
593
|
-
'-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
}
|
|
589
|
+
'- Codex users: make sure `codex mcp add distributionos --url https://distributionos.dev/api/mcp` and `codex mcp login distributionos` have run, then fully quit and reopen Codex Desktop before checking the connection.',
|
|
590
|
+
'- Claude Code users: if .mcp.json was created or updated, restart or reconnect Claude Code and approve the project MCP server if asked.',
|
|
591
|
+
'- Other MCP-aware agents: reconnect the agent from this repo so it can load the DistributionOS MCP server.',
|
|
592
|
+
'- On the review page, copy the connection check prompt into the fresh/reconnected agent chat.',
|
|
593
|
+
'- If your agent only sees DistributionOS authentication tools, copy the auth prompt from the review page and complete OAuth before continuing.',
|
|
594
|
+
'- Then copy the verification prompt from the review page and ask your agent to call check_distributionos_connection, get_agent_instructions, and get_analytics_summary.',
|
|
595
|
+
'- DistributionOS will wait for a successful app-scoped MCP call before you continue to Brain Doc review.',
|
|
596
|
+
'- After review, come back to this repo and inspect git diff before committing, pushing, or deploying.',
|
|
597
|
+
);
|
|
598
|
+
} else {
|
|
599
|
+
lines.push(
|
|
600
|
+
'- Reconnect DistributionOS OAuth or provide an app-scoped token, then rerun setup so initialization can be submitted.',
|
|
601
|
+
'- No DistributionOS review page is ready yet.',
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
return `${lines.join('\n')}\n`;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function oneLine(value) {
|
|
608
|
+
return String(value).split(/\r?\n/).filter(Boolean).slice(-1)[0]?.slice(0, 240) ?? '';
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function absoluteDistributionOsUrl(pathOrUrl, apiBase) {
|
|
612
|
+
try {
|
|
613
|
+
return new URL(pathOrUrl, apiBase || DEFAULT_API_BASE).toString();
|
|
614
|
+
} catch {
|
|
615
|
+
return String(pathOrUrl);
|
|
616
|
+
}
|
|
617
|
+
}
|
package/src/constants.js
CHANGED
|
@@ -6,9 +6,9 @@ export const CLI_PACKAGE_NAME = '@distributionos/cli';
|
|
|
6
6
|
export const CLI_LOCAL_VERSION = typeof cliPackageJson.version === 'string' ? cliPackageJson.version : '0.0.0-local';
|
|
7
7
|
export const DEFAULT_API_BASE = 'https://distributionos.dev';
|
|
8
8
|
export const MCP_SERVER_URL = 'https://distributionos.dev/api/mcp';
|
|
9
|
-
export const CURRENT_BOOTSTRAP_VERSION = '2026.06.
|
|
10
|
-
export const CURRENT_ANALYTICS_CONTRACT_VERSION = '2026.06.
|
|
11
|
-
export const CURRENT_SETUP_CONTRACT_VERSION = '2026.
|
|
9
|
+
export const CURRENT_BOOTSTRAP_VERSION = '2026.06.27';
|
|
10
|
+
export const CURRENT_ANALYTICS_CONTRACT_VERSION = '2026.06.20';
|
|
11
|
+
export const CURRENT_SETUP_CONTRACT_VERSION = '2026.07.01';
|
|
12
12
|
|
|
13
13
|
export const TOKEN_ENV_NAMES = [
|
|
14
14
|
'DISTRIBUTIONOS_API_KEY',
|
|
@@ -49,11 +49,11 @@ export const PRIVATE_ROUTE_PATTERNS = [
|
|
|
49
49
|
'/products/**',
|
|
50
50
|
'/project/**',
|
|
51
51
|
'/projects/**',
|
|
52
|
-
'/workspace/**',
|
|
53
|
-
'/workspaces/**',
|
|
54
|
-
'/workflow/**',
|
|
55
|
-
'/workflows/**',
|
|
56
|
-
'/customer/**',
|
|
52
|
+
'/workspace/**',
|
|
53
|
+
'/workspaces/**',
|
|
54
|
+
'/workflow/**',
|
|
55
|
+
'/workflows/**',
|
|
56
|
+
'/customer/**',
|
|
57
57
|
'/customers/**',
|
|
58
58
|
'/client/**',
|
|
59
59
|
'/clients/**',
|