@dharma-ai-labs/agent-fabric 0.2.190 → 0.2.191
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/AGENT_FABRIC_ONBOARDING.md +4 -2
- package/dist/cliVersionContract.test.d.ts +2 -0
- package/dist/cliVersionContract.test.d.ts.map +1 -0
- package/dist/cliVersionContract.test.js +17 -0
- package/dist/cliVersionContract.test.js.map +1 -0
- package/dist/codexSetupLaunchReservation.d.ts +12 -0
- package/dist/codexSetupLaunchReservation.d.ts.map +1 -0
- package/dist/codexSetupLaunchReservation.js +76 -0
- package/dist/codexSetupLaunchReservation.js.map +1 -0
- package/dist/codexSetupLaunchReservation.test.d.ts +2 -0
- package/dist/codexSetupLaunchReservation.test.d.ts.map +1 -0
- package/dist/codexSetupLaunchReservation.test.js +168 -0
- package/dist/codexSetupLaunchReservation.test.js.map +1 -0
- package/dist/codexSetupOwnedConnection.d.ts +4 -1
- package/dist/codexSetupOwnedConnection.d.ts.map +1 -1
- package/dist/codexSetupOwnedConnection.js +16 -3
- package/dist/codexSetupOwnedConnection.js.map +1 -1
- package/dist/codexSetupOwnedConnection.test.js +14 -0
- package/dist/codexSetupOwnedConnection.test.js.map +1 -1
- package/dist/codexSetupPortalIngress.test.d.ts +2 -0
- package/dist/codexSetupPortalIngress.test.d.ts.map +1 -0
- package/dist/codexSetupPortalIngress.test.js +355 -0
- package/dist/codexSetupPortalIngress.test.js.map +1 -0
- package/dist/codexSetupPortalPreparation.d.ts +28 -0
- package/dist/codexSetupPortalPreparation.d.ts.map +1 -0
- package/dist/codexSetupPortalPreparation.js +101 -0
- package/dist/codexSetupPortalPreparation.js.map +1 -0
- package/dist/codexSetupPortalPreparation.test.d.ts +2 -0
- package/dist/codexSetupPortalPreparation.test.d.ts.map +1 -0
- package/dist/codexSetupPortalPreparation.test.js +173 -0
- package/dist/codexSetupPortalPreparation.test.js.map +1 -0
- package/dist/codexSetupPublicStdin.d.ts +6 -0
- package/dist/codexSetupPublicStdin.d.ts.map +1 -0
- package/dist/codexSetupPublicStdin.js +36 -0
- package/dist/codexSetupPublicStdin.js.map +1 -0
- package/dist/codexSetupPublicStdin.test.d.ts +2 -0
- package/dist/codexSetupPublicStdin.test.d.ts.map +1 -0
- package/dist/codexSetupPublicStdin.test.js +40 -0
- package/dist/codexSetupPublicStdin.test.js.map +1 -0
- package/dist/codexSetupUserPrompt.d.ts +2 -0
- package/dist/codexSetupUserPrompt.d.ts.map +1 -1
- package/dist/codexSetupUserPrompt.js +13 -7
- package/dist/codexSetupUserPrompt.js.map +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +187 -4
- package/dist/index.js.map +1 -1
- package/dist/onboardingContract.test.js +9 -0
- package/dist/onboardingContract.test.js.map +1 -1
- package/dist/schemas/setup-claim-request.schema.json +10 -0
- package/dist/schemas/setup-host-preparation.schema.json +51 -0
- package/dist/schemas/setup-launch-reservation.schema.json +29 -0
- package/dist/usage.js +1 -1
- package/dist/usage.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -93,6 +93,9 @@ import { assertBootstrapHostSource, captureBootstrapHostChild, currentBootstrapH
|
|
|
93
93
|
import { startCodexSetupNativeHost } from './codexSetupNativeHost.js';
|
|
94
94
|
import { openCodexSetupOwnedConnection } from './codexSetupOwnedConnection.js';
|
|
95
95
|
import { captureCodexSetupUserPrompt, captureCodexSetupPromptBinding } from './codexSetupUserPrompt.js';
|
|
96
|
+
import { requestCodexSetupHostPreparation } from './codexSetupPortalPreparation.js';
|
|
97
|
+
import { readCodexSetupPublicStdin } from './codexSetupPublicStdin.js';
|
|
98
|
+
import { reserveCodexSetupLaunch } from './codexSetupLaunchReservation.js';
|
|
96
99
|
export { startCodexSetupNativeHost } from './codexSetupNativeHost.js';
|
|
97
100
|
export { openCodexSetupVaultJournal } from './codexSetupVaultJournal.js';
|
|
98
101
|
import { createCodexSetupReadinessOwner } from './codexSetupReadiness.js';
|
|
@@ -101,7 +104,7 @@ import { assertCodexSetupExecutionLease } from './codexSetupAdmission.js';
|
|
|
101
104
|
import { parseLocalCodexSetupReadiness } from '@dharma-ai-labs/agent-fabric-local-vault/setup-readiness';
|
|
102
105
|
import { writeBootstrapHostJson, writeBootstrapHostText } from './bootstrapHostFiles.js';
|
|
103
106
|
export { openCooperativeInboxSession } from './cooperativeInboxSession.js';
|
|
104
|
-
const VERSION = '0.2.
|
|
107
|
+
const VERSION = '0.2.191';
|
|
105
108
|
const USAGE = CLI_USAGE;
|
|
106
109
|
const execFileAsync = promisify(execFile);
|
|
107
110
|
const LOCAL_PROVIDER_IDS = ['codex', 'claude', 'agy', 'hermes'];
|
|
@@ -159,13 +162,17 @@ function print(value) {
|
|
|
159
162
|
}
|
|
160
163
|
export function commandExitCode(argv, value) {
|
|
161
164
|
const { positional, flags } = parseCliOptions(argv);
|
|
162
|
-
|
|
165
|
+
const setupIngress = positional[0] === 'setup' && positional[1] === 'codex';
|
|
166
|
+
if ((!setupIngress && positional[0] !== 'bootstrap') || flags.has('help') || flags.has('version'))
|
|
163
167
|
return 0;
|
|
164
168
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
165
169
|
return 1;
|
|
166
170
|
const receipt = value;
|
|
167
171
|
if (receipt.ok !== true)
|
|
168
172
|
return 1;
|
|
173
|
+
if (setupIngress)
|
|
174
|
+
return flags.has('dry-run') && receipt.stage === 'setup_host_plan' && receipt.effects === false
|
|
175
|
+
? 0 : receipt.stage === 'complete' && receipt.sharedRepositoryReady === true ? 0 : 1;
|
|
169
176
|
if (flags.has('setup-reference') && flags.has('dry-run') && receipt.stage === 'plan' && receipt.effects === false)
|
|
170
177
|
return 0;
|
|
171
178
|
return flags.has('complete') && (receipt.stage !== 'complete' || receipt.sharedRepositoryReady !== true) ? 1 : 0;
|
|
@@ -2907,6 +2914,7 @@ export async function openCodexBootstrapNativeHost(input) {
|
|
|
2907
2914
|
void settled.catch(() => { });
|
|
2908
2915
|
return Object.freeze({ threadId: host.threadId, turnId: host.turnId, close, settled,
|
|
2909
2916
|
initialUserPromptSha256: host.initialUserPromptSha256,
|
|
2917
|
+
get verifiedCompletion() { return ownedHost.verifiedCompletion; },
|
|
2910
2918
|
get diagnostics() { return ownedHost.diagnostics; } });
|
|
2911
2919
|
}
|
|
2912
2920
|
catch {
|
|
@@ -3003,6 +3011,7 @@ export async function startCodexBootstrapNativeHost(input) {
|
|
|
3003
3011
|
const root = resolve(dharmaHome(), 'vault');
|
|
3004
3012
|
let completion;
|
|
3005
3013
|
let ownerScope;
|
|
3014
|
+
let verifiedCompletion;
|
|
3006
3015
|
const failures = [];
|
|
3007
3016
|
const observeFailure = failure => { if (failures.length < 16)
|
|
3008
3017
|
failures.push(failure); };
|
|
@@ -3040,11 +3049,19 @@ export async function startCodexBootstrapNativeHost(input) {
|
|
|
3040
3049
|
return completion.execute(lease);
|
|
3041
3050
|
},
|
|
3042
3051
|
verifyReadiness: async (receiptId, intent, digest) => {
|
|
3052
|
+
verifiedCompletion = undefined;
|
|
3043
3053
|
if (!completion)
|
|
3044
3054
|
return false;
|
|
3045
|
-
|
|
3055
|
+
const verified = await completion.verify(receiptId, intent, digest);
|
|
3056
|
+
// This callback runs after the original admission's journal readback. A
|
|
3057
|
+
// completed native turn or another process's device config cannot set it.
|
|
3058
|
+
if (verified)
|
|
3059
|
+
verifiedCompletion = Object.freeze({ operationId: intent.operationId,
|
|
3060
|
+
setupReference: intent.setupReference, readinessReceiptId: receiptId });
|
|
3061
|
+
return verified;
|
|
3046
3062
|
}, });
|
|
3047
|
-
return Object.freeze({ ...host, get
|
|
3063
|
+
return Object.freeze({ ...host, get verifiedCompletion() { return verifiedCompletion; },
|
|
3064
|
+
get diagnostics() { return Object.freeze([...failures]); } });
|
|
3048
3065
|
}
|
|
3049
3066
|
async function assertBootstrapStartupAnchor(organizationId, deviceId, step) {
|
|
3050
3067
|
const home = dharmaHome();
|
|
@@ -3080,6 +3097,166 @@ async function assertBootstrapStartupAnchor(organizationId, deviceId, step) {
|
|
|
3080
3097
|
throw new Error('relay_runtime_upgrade_required: upgrade the existing startup anchor before connecting another repository.');
|
|
3081
3098
|
}
|
|
3082
3099
|
}
|
|
3100
|
+
/** Official public ingress. Only this local client, never the website or model,
|
|
3101
|
+
* owns the resulting native connection and its original setup execution lease. */
|
|
3102
|
+
async function codexPortalSetup(flags) {
|
|
3103
|
+
const allowed = new Set(['portal-url', 'organization-id', 'setup-reference', 'workspace',
|
|
3104
|
+
'maximum-provider-cost-cents', 'dry-run', 'stdin']);
|
|
3105
|
+
if ([...flags.keys()].some(key => !allowed.has(key)) || flags.get('stdin') !== true
|
|
3106
|
+
|| flags.has('dry-run') && flags.get('dry-run') !== true
|
|
3107
|
+
|| flags.has('maximum-provider-cost-cents') && typeof flags.get('maximum-provider-cost-cents') !== 'string') {
|
|
3108
|
+
throw new Error('codex_setup_ingress_options_invalid');
|
|
3109
|
+
}
|
|
3110
|
+
const controller = new AbortController(), stop = () => controller.abort();
|
|
3111
|
+
for (const signal of ['SIGINT', 'SIGTERM'])
|
|
3112
|
+
process.once(signal, stop);
|
|
3113
|
+
let host;
|
|
3114
|
+
try {
|
|
3115
|
+
const origin = normalizeHqUrl(required(flags, 'portal-url'));
|
|
3116
|
+
const organizationId = required(flags, 'organization-id'), setupReference = required(flags, 'setup-reference');
|
|
3117
|
+
const prompt = await readCodexSetupPublicStdin(process.stdin, controller.signal);
|
|
3118
|
+
const prepared = await requestCodexSetupHostPreparation({ origin, organizationId, setupReference,
|
|
3119
|
+
cliVersion: VERSION, initialUserPrompt: prompt, signal: controller.signal });
|
|
3120
|
+
if (flags.has('dry-run'))
|
|
3121
|
+
return { ok: true, stage: 'setup_host_plan', effects: false,
|
|
3122
|
+
enrollmentChanged: false, requiresHostExecutionAdmission: true, requiresRecipientApproval: true,
|
|
3123
|
+
promptSha256: prepared.promptSha256, initialUserPromptSha256: prepared.initialUserPromptSha256,
|
|
3124
|
+
portalContextSha256: prepared.portalContextSha256,
|
|
3125
|
+
organizationId, setupReference, recipientMembershipId: prepared.intent.recipientMembershipId,
|
|
3126
|
+
repositoryFingerprint: prepared.intent.repositoryFingerprint, expiresAt: prepared.intent.expiresAt };
|
|
3127
|
+
if (process.platform !== 'linux' || !process.getuid?.() || !process.env.DHARMA_HOME || !process.env.CODEX_HOME) {
|
|
3128
|
+
throw new Error('codex_setup_ingress_context_unqualified');
|
|
3129
|
+
}
|
|
3130
|
+
const workspace = await realpath(String(flags.get('workspace') || '.'));
|
|
3131
|
+
const home = dharmaHome(), providerHome = process.env.CODEX_HOME, uid = process.getuid();
|
|
3132
|
+
const environment = { home: process.env.DHARMA_HOME, provider: providerHome };
|
|
3133
|
+
const currentDirectory = process.cwd(), file = fileURLToPath(import.meta.url), runtime = await lstat(file, { bigint: true });
|
|
3134
|
+
const cents = boundedInteger(flags.get('maximum-provider-cost-cents'), 25, 1, 25, '--maximum-provider-cost-cents');
|
|
3135
|
+
const privateDirectory = async (path, privateMode) => {
|
|
3136
|
+
if (!isAbsolute(path) || resolve(path) !== path || !path.startsWith(resolve(homedir()) + '/'))
|
|
3137
|
+
return false;
|
|
3138
|
+
const info = await lstat(path);
|
|
3139
|
+
return info.isDirectory() && !info.isSymbolicLink() && info.uid === uid
|
|
3140
|
+
&& (info.mode & (privateMode ? 0o077 : 0o022)) === 0 && await realpath(path) === path;
|
|
3141
|
+
};
|
|
3142
|
+
const current = async () => {
|
|
3143
|
+
if (controller.signal.aborted || Date.now() >= Date.parse(prepared.intent.expiresAt)
|
|
3144
|
+
|| process.getuid?.() !== uid || process.cwd() !== currentDirectory
|
|
3145
|
+
|| process.env.DHARMA_HOME !== environment.home || process.env.CODEX_HOME !== environment.provider)
|
|
3146
|
+
return false;
|
|
3147
|
+
try {
|
|
3148
|
+
const now = await lstat(file, { bigint: true });
|
|
3149
|
+
return now.isFile() && !now.isSymbolicLink() && now.dev === runtime.dev && now.ino === runtime.ino
|
|
3150
|
+
&& now.size === runtime.size && now.mtimeNs === runtime.mtimeNs && now.ctimeNs === runtime.ctimeNs
|
|
3151
|
+
&& await privateDirectory(home, true) && await privateDirectory(providerHome, false)
|
|
3152
|
+
&& (await preflightBootstrapWorkspaceIdentity(workspace)).fingerprint === prepared.intent.repositoryFingerprint;
|
|
3153
|
+
}
|
|
3154
|
+
catch {
|
|
3155
|
+
return false;
|
|
3156
|
+
}
|
|
3157
|
+
};
|
|
3158
|
+
if (!await current() || await detectBootstrapProvider(workspace) !== 'codex') {
|
|
3159
|
+
throw new Error('codex_setup_ingress_context_unqualified');
|
|
3160
|
+
}
|
|
3161
|
+
// Read only public account identity through the official owned provider.
|
|
3162
|
+
// It is not the portal recipient, a credential copy or a nested login shell.
|
|
3163
|
+
const account = await openCodexSetupOwnedConnection({ workspace, deviceHome: home,
|
|
3164
|
+
accountSelection: 'current_authenticated', signal: controller.signal, current });
|
|
3165
|
+
const expectedAccountEmail = account.accountEmail;
|
|
3166
|
+
await account.close();
|
|
3167
|
+
if (!await current())
|
|
3168
|
+
throw new Error('codex_setup_ingress_context_unqualified');
|
|
3169
|
+
const portalArgs = ['bootstrap', '--portal-url', origin, '--organization-id', organizationId,
|
|
3170
|
+
'--setup-reference', setupReference, '--setup-recipient-membership-id', prepared.intent.recipientMembershipId,
|
|
3171
|
+
'--setup-scope-digest', prepared.intent.scopeDigest, '--setup-contract-digest', prepared.intent.contractDigest,
|
|
3172
|
+
'--workspace', workspace, '--repository-url-base64url',
|
|
3173
|
+
Buffer.from(prepared.binding.repositoryRemoteUrl).toString('base64url'),
|
|
3174
|
+
'--policy-revision', prepared.intent.policyRevision, '--complete'];
|
|
3175
|
+
host = await openCodexBootstrapFromPortalCommand({ workspace, name: 'setup-' + setupReference.slice(0, 8),
|
|
3176
|
+
intent: prepared.intent, portalArgs, initialUserPrompt: prepared.initialUserPrompt,
|
|
3177
|
+
initialUserPromptBinding: prepared.binding, expectedAccountEmail, signal: controller.signal,
|
|
3178
|
+
current, maximumProviderCostCents: cents,
|
|
3179
|
+
reserve: async (operationId, requested) => {
|
|
3180
|
+
if (operationId !== prepared.intent.operationId || requested !== cents || !await current())
|
|
3181
|
+
return false;
|
|
3182
|
+
// Recheck the unchanged source consent immediately before the native
|
|
3183
|
+
// turn reservation. This is still not browser approval or a policy lock.
|
|
3184
|
+
const rechecked = await requestCodexSetupHostPreparation({ origin, organizationId, setupReference,
|
|
3185
|
+
cliVersion: VERSION, initialUserPrompt: prompt, signal: controller.signal });
|
|
3186
|
+
if (rechecked.promptSha256 !== prepared.promptSha256
|
|
3187
|
+
|| canonicalize(rechecked.binding) !== canonicalize(prepared.binding)
|
|
3188
|
+
|| canonicalize({ ...rechecked.intent, operationId: prepared.intent.operationId,
|
|
3189
|
+
hostContextId: prepared.intent.hostContextId, issuedAt: prepared.intent.issuedAt }) !== canonicalize(prepared.intent))
|
|
3190
|
+
return false;
|
|
3191
|
+
return reserveCodexSetupLaunch({ home, organizationId, setupReference, operationId,
|
|
3192
|
+
promptSha256: prepared.promptSha256, cents, current });
|
|
3193
|
+
} });
|
|
3194
|
+
await host.settled;
|
|
3195
|
+
await host.close();
|
|
3196
|
+
if (!await current())
|
|
3197
|
+
throw new Error('codex_setup_ingress_context_unqualified');
|
|
3198
|
+
const completed = host.verifiedCompletion;
|
|
3199
|
+
if (!completed || completed.operationId !== prepared.intent.operationId
|
|
3200
|
+
|| completed.setupReference !== setupReference || !UUID_PATTERN.test(completed.readinessReceiptId)) {
|
|
3201
|
+
return { ok: false, stage: 'setup_host_unconfirmed', code: 'codex_setup_ingress_readiness_unconfirmed',
|
|
3202
|
+
nativeThreadId: host.threadId, nativeTurnId: host.turnId };
|
|
3203
|
+
}
|
|
3204
|
+
const config = await readDeviceConfig();
|
|
3205
|
+
if (!config || config.organizationId !== organizationId || normalizeHqUrl(config.hqUrl) !== origin
|
|
3206
|
+
|| config.setupClaimReference !== setupReference
|
|
3207
|
+
|| config.setupClaimRepositoryFingerprint !== prepared.intent.repositoryFingerprint) {
|
|
3208
|
+
return { ok: false, stage: 'setup_host_unconfirmed', effects: 'unconfirmed',
|
|
3209
|
+
code: 'codex_setup_ingress_readiness_unconfirmed', setupReference,
|
|
3210
|
+
nativeThreadId: host.threadId, nativeTurnId: host.turnId };
|
|
3211
|
+
}
|
|
3212
|
+
// One supported grant-free reconciliation, only for this consumed reference.
|
|
3213
|
+
// No replacement identity, grant replay or automatic provider-turn retry.
|
|
3214
|
+
const resume = parseCliOptions(portalArgs).flags;
|
|
3215
|
+
for (const key of ['setup-reference', 'setup-recipient-membership-id', 'setup-scope-digest', 'setup-contract-digest'])
|
|
3216
|
+
resume.delete(key);
|
|
3217
|
+
resume.set('resume', true);
|
|
3218
|
+
if (!await current())
|
|
3219
|
+
throw new Error('codex_setup_ingress_context_unqualified');
|
|
3220
|
+
// The owned native host is closed. This is now ordinary enrolled-device
|
|
3221
|
+
// reconciliation: restore Node's normal termination instead of suppressing
|
|
3222
|
+
// SIGINT/SIGTERM with a controller that bootstrap does not consume.
|
|
3223
|
+
for (const signal of ['SIGINT', 'SIGTERM'])
|
|
3224
|
+
process.removeListener(signal, stop);
|
|
3225
|
+
if (controller.signal.aborted)
|
|
3226
|
+
throw new Error('codex_setup_ingress_context_unqualified');
|
|
3227
|
+
const result = await bootstrap(resume);
|
|
3228
|
+
if (!result || typeof result !== 'object' || Array.isArray(result)) {
|
|
3229
|
+
return { ok: false, stage: 'setup_host_unconfirmed', code: 'codex_setup_ingress_readiness_unconfirmed' };
|
|
3230
|
+
}
|
|
3231
|
+
return { ...result, portalIngress: { promptSha256: prepared.promptSha256,
|
|
3232
|
+
portalContextSha256: prepared.portalContextSha256,
|
|
3233
|
+
initialUserPromptSha256: prepared.initialUserPromptSha256, operationId: prepared.intent.operationId,
|
|
3234
|
+
readinessReceiptId: completed.readinessReceiptId,
|
|
3235
|
+
nativeThreadId: host.threadId, nativeTurnId: host.turnId,
|
|
3236
|
+
reservedProviderCostCents: cents, actualUsd: null, liability: 'UNSETTLED_NOT_RELEASED',
|
|
3237
|
+
hardInvoiceCapProven: false } };
|
|
3238
|
+
}
|
|
3239
|
+
catch (error) {
|
|
3240
|
+
const known = new Set(['codex_setup_ingress_options_invalid', 'codex_setup_public_input_unavailable',
|
|
3241
|
+
'codex_setup_public_prompt_invalid', 'codex_setup_portal_preparation_unavailable',
|
|
3242
|
+
'codex_setup_ingress_context_unqualified', 'codex_setup_owned_start_unqualified',
|
|
3243
|
+
'codex_setup_owned_close_unconfirmed', 'codex_setup_owned_completion_unconfirmed']);
|
|
3244
|
+
return { ok: false, stage: 'setup_host_unconfirmed',
|
|
3245
|
+
code: error instanceof Error && known.has(error.message) ? error.message : 'codex_setup_ingress_unconfirmed',
|
|
3246
|
+
requiresSupportedRecovery: true };
|
|
3247
|
+
}
|
|
3248
|
+
finally {
|
|
3249
|
+
controller.abort();
|
|
3250
|
+
for (const signal of ['SIGINT', 'SIGTERM'])
|
|
3251
|
+
process.removeListener(signal, stop);
|
|
3252
|
+
try {
|
|
3253
|
+
await host?.close();
|
|
3254
|
+
}
|
|
3255
|
+
catch {
|
|
3256
|
+
return { ok: false, stage: 'setup_host_unconfirmed', code: 'codex_setup_owned_close_unconfirmed' };
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3083
3260
|
async function bootstrap(flags, hostScope) {
|
|
3084
3261
|
const step = (operation) => hostScope ? hostScope.step(operation) : operation();
|
|
3085
3262
|
await hostScope?.assert();
|
|
@@ -8795,6 +8972,12 @@ export async function run(argv) {
|
|
|
8795
8972
|
await originalCodexSetupSessionSender(setupScope);
|
|
8796
8973
|
return setupScope.step(() => bootstrap(flags, setupScope));
|
|
8797
8974
|
}
|
|
8975
|
+
if (command === 'setup' && subcommand === 'codex') {
|
|
8976
|
+
if (positional.length !== 2 || [...repeated.values()].some(values => values.length !== 1)) {
|
|
8977
|
+
throw new Error('codex_setup_ingress_options_invalid');
|
|
8978
|
+
}
|
|
8979
|
+
return codexPortalSetup(flags);
|
|
8980
|
+
}
|
|
8798
8981
|
if (command === 'relay' && subcommand === 'container-entrypoint') {
|
|
8799
8982
|
if (positional.length === 2 && flags.size === 1 && flags.get('dry-run') === true) {
|
|
8800
8983
|
return { ok: true, stage: 'container_entrypoint_plan', started: false,
|