@vimhead.dev/norn-cli 0.1.0-tip.35390859630.1 → 0.1.0-tip.35568631545.1
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/assets/README.md +36 -15
- package/assets/docs/README.md +4 -4
- package/assets/docs/agents.md +39 -11
- package/assets/docs/cli.md +6 -6
- package/assets/docs/composition.md +12 -12
- package/assets/docs/persistence.md +31 -17
- package/assets/docs/providers.md +2 -2
- package/assets/docs/recovery.md +1 -1
- package/assets/docs/workflows.md +29 -17
- package/assets/examples/agent-then-analysis/README.md +8 -8
- package/assets/examples/agent-then-analysis/plugin.ts +23 -21
- package/assets/examples/caller-selected-continuation/README.md +11 -10
- package/assets/examples/caller-selected-continuation/caller.ts +15 -13
- package/assets/examples/caller-selected-continuation/producer.ts +11 -8
- package/assets/examples/coordinating-multiple-agents/README.md +27 -17
- package/assets/examples/coordinating-multiple-agents/plugin.ts +64 -41
- package/assets/examples/coordinating-multiple-agents/queue-tools.ts +43 -0
- package/assets/examples/coordinating-multiple-agents/work-queue.ts +60 -65
- package/assets/examples/getting-started/README.md +25 -10
- package/assets/examples/getting-started/plugin.ts +30 -11
- package/assets/examples/minimal-workflow/README.md +7 -6
- package/assets/examples/minimal-workflow/plugin.ts +8 -5
- package/assets/examples/shared-state/README.md +5 -5
- package/assets/examples/shared-state/plugin.ts +38 -25
- package/assets/examples/shared-state/shared-state.ts +30 -49
- package/assets/examples/shared-state/state-tools.ts +68 -0
- package/assets/examples/worktree-development-loop/README.md +14 -12
- package/assets/examples/worktree-development-loop/scope.ts +1 -1
- package/assets/examples/worktree-development-loop/workflows/development-loop/execute.ts +3 -3
- package/assets/examples/worktree-development-loop/workflows/development-loop/repository.ts +6 -4
- package/assets/examples/worktree-development-loop/workflows/implementation/execute.ts +12 -11
- package/assets/examples/worktree-development-loop/workflows/implementation/schema.ts +2 -3
- package/assets/examples/worktree-development-loop/workflows/planning/execute.ts +10 -6
- package/assets/examples/worktree-development-loop/workflows/review/execute.ts +14 -11
- package/assets/examples/worktree-development-loop/workflows/review/schema.ts +1 -2
- package/assets/examples/worktree-development-loop/workflows/review-router/execute.ts +16 -15
- package/assets/examples/worktree-development-loop/workflows/review-router/schema.ts +1 -2
- package/assets/package.json +1 -1
- package/assets/packages/cli/src/cli.ts +9 -3
- package/assets/packages/cli/src/generated-build-info.ts +2 -2
- package/assets/packages/cli/src/internal/agents.ts +24 -32
- package/assets/packages/cli/src/internal/commands.ts +2 -14
- package/assets/packages/cli/src/internal/engine.ts +35 -49
- package/assets/packages/cli/src/internal/execution-context.ts +64 -0
- package/assets/packages/cli/src/internal/logs.ts +1 -1
- package/assets/packages/cli/src/internal/run-log.ts +1 -1
- package/assets/packages/cli/src/internal/run-state.ts +7 -2
- package/assets/packages/cli/src/internal/worker-directory.ts +17 -0
- package/assets/packages/cli/src/internal/workflow-registry.ts +24 -9
- package/assets/packages/cli/src/internal/working-directory.ts +6 -0
- package/assets/packages/cli/src/workflow-loader.ts +1 -2
- package/assets/packages/sdk/src/api.ts +65 -91
- package/assets/packages/sdk/src/index.ts +0 -3
- package/assets/tests/workflow-ref.test.ts +9 -9
- package/dist/cli.js +8 -3
- package/dist/generated-build-info.d.ts +2 -2
- package/dist/generated-build-info.js +2 -2
- package/dist/internal/agents.d.ts +0 -4
- package/dist/internal/agents.js +30 -46
- package/dist/internal/commands.d.ts +0 -4
- package/dist/internal/commands.js +2 -10
- package/dist/internal/engine.d.ts +1 -1
- package/dist/internal/engine.js +29 -43
- package/dist/internal/execution-context.d.ts +26 -0
- package/dist/internal/execution-context.js +55 -0
- package/dist/internal/file-coordinator.d.ts +17 -0
- package/dist/internal/file-coordinator.js +162 -0
- package/dist/internal/logs.d.ts +1 -1
- package/dist/internal/run-log.d.ts +1 -1
- package/dist/internal/run-state.d.ts +2 -0
- package/dist/internal/run-state.js +5 -2
- package/dist/internal/worker-directory.d.ts +1 -0
- package/dist/internal/worker-directory.js +26 -0
- package/dist/internal/workflow-registry.d.ts +13 -3
- package/dist/internal/workflow-registry.js +12 -7
- package/dist/internal/working-directory.d.ts +1 -0
- package/dist/internal/working-directory.js +9 -0
- package/dist/workflow-loader.js +1 -2
- package/package.json +2 -2
- package/assets/docs/resources.md +0 -46
- package/assets/examples/coordinating-multiple-agents/queue-adapter.ts +0 -51
- package/assets/examples/shared-state/state-adapter.ts +0 -76
- package/assets/packages/cli/src/internal/artifacts.ts +0 -26
- package/assets/packages/cli/src/internal/resource-bindings.ts +0 -35
- package/assets/packages/cli/src/internal/run.ts +0 -127
- package/assets/packages/cli/src/resources.ts +0 -69
- package/assets/packages/sdk/src/agent-resource-adapter.ts +0 -11
- package/assets/packages/sdk/src/resources.ts +0 -20
- package/dist/internal/artifacts.d.ts +0 -10
- package/dist/internal/artifacts.js +0 -29
- package/dist/internal/resource-bindings.d.ts +0 -13
- package/dist/internal/resource-bindings.js +0 -34
- package/dist/internal/run.d.ts +0 -49
- package/dist/internal/run.js +0 -103
- package/dist/resources.d.ts +0 -11
- package/dist/resources.js +0 -100
- /package/assets/packages/{sdk/src/files.ts → cli/src/internal/file-coordinator.ts} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CreateAgentSessionOptions, EventBus, PromptOptions } from "@earendil-works/pi-coding-agent";
|
|
1
|
+
import type { CreateAgentSessionOptions, EventBus, PromptOptions, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { Type, type StaticDecode, type StaticEncode, type TCodec, type TSchema } from "typebox";
|
|
3
3
|
import type { TLocalizedValidationError } from "typebox/error";
|
|
4
4
|
import { createWorkflowTransition } from "@vimhead.dev/norn-core/workflow-transition";
|
|
@@ -15,16 +15,17 @@ export type NornWorkflowAnyGate = {
|
|
|
15
15
|
readonly fields?: readonly string[];
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
export type
|
|
18
|
+
export type NornWorkflowPaths = {
|
|
19
|
+
/** Absolute project root. Excluded from run checkpoints and rollback. */
|
|
20
|
+
readonly project: string;
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
readonly
|
|
22
|
+
/** Absolute, initially empty workspace. Saved in run checkpoints and restored on rollback. */
|
|
23
|
+
readonly workspace: string;
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
export type NornWorkflowDeclaration<
|
|
25
27
|
Id extends string = string,
|
|
26
28
|
ArgsSchema extends TSchema = TSchema,
|
|
27
|
-
IsolationMode extends NornWorkflowIsolationMode = NornWorkflowIsolationMode,
|
|
28
29
|
ConfigSchema extends TSchema | undefined = TSchema | undefined,
|
|
29
30
|
Scope extends NornWorkflowScopeInfo | undefined = NornWorkflowScopeInfo | undefined,
|
|
30
31
|
> = {
|
|
@@ -35,12 +36,11 @@ export type NornWorkflowDeclaration<
|
|
|
35
36
|
readonly instructions?: string;
|
|
36
37
|
readonly args: ArgsSchema;
|
|
37
38
|
readonly gate?: NornWorkflowGate<ArgsSchema> & {
|
|
38
|
-
describe?(context: NornWorkflowContext<ArgsSchema,
|
|
39
|
+
describe?(context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>): MaybePromise<string>;
|
|
39
40
|
};
|
|
40
|
-
readonly isolation: NornWorkflowIsolation<IsolationMode>;
|
|
41
41
|
readonly config?: ConfigSchema;
|
|
42
42
|
readonly scope: Scope;
|
|
43
|
-
execute(context: NornWorkflowContext<ArgsSchema,
|
|
43
|
+
execute(context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>): MaybePromise<WorkflowResult>;
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
export type NornAnyWorkflowDeclaration = Pick<NornWorkflowDeclaration, keyof NornWorkflowDeclaration> & ((args: never) => NornRunNext);
|
|
@@ -69,37 +69,37 @@ type NornWorkflowInstructions =
|
|
|
69
69
|
| { readonly isEntrypoint: true; readonly instructions: string }
|
|
70
70
|
| { readonly isEntrypoint: false; readonly instructions?: string };
|
|
71
71
|
|
|
72
|
-
type RunForIsolation<Mode extends NornWorkflowIsolationMode> = Mode extends "project" ? NornProjectRun : NornRun;
|
|
73
72
|
type WorkflowConfig<Schema extends TSchema | undefined> = Schema extends TSchema ? StaticDecode<Schema> : undefined;
|
|
74
73
|
|
|
75
74
|
export type NornWorkflowContext<
|
|
76
75
|
ArgsSchema extends TSchema = TSchema,
|
|
77
|
-
Mode extends NornWorkflowIsolationMode = NornWorkflowIsolationMode,
|
|
78
76
|
ConfigSchema extends TSchema | undefined = TSchema | undefined,
|
|
79
77
|
Scope extends NornWorkflowScopeInfo | undefined = NornWorkflowScopeInfo | undefined,
|
|
80
78
|
> = {
|
|
81
79
|
readonly args: StaticDecode<ArgsSchema>;
|
|
82
80
|
readonly config: WorkflowConfig<ConfigSchema>;
|
|
83
|
-
readonly
|
|
81
|
+
readonly paths: NornWorkflowPaths;
|
|
82
|
+
readonly agents: NornAgents;
|
|
83
|
+
readonly commands: NornCommands;
|
|
84
|
+
readonly logs: NornLogs;
|
|
85
|
+
readonly run: NornRun;
|
|
84
86
|
} & (Scope extends NornWorkflowScopeInfo ? {
|
|
85
87
|
readonly scope: { readonly id: Scope["id"]; readonly config: WorkflowConfig<Scope["config"]> };
|
|
86
88
|
} : {});
|
|
87
89
|
|
|
88
90
|
export type NornWorkflowDefinition<
|
|
89
|
-
|
|
91
|
+
Name extends string = string,
|
|
90
92
|
ArgsSchema extends TSchema = TSchema,
|
|
91
|
-
IsolationMode extends NornWorkflowIsolationMode = NornWorkflowIsolationMode,
|
|
92
93
|
ConfigSchema extends TSchema | undefined = undefined,
|
|
93
94
|
Scope extends NornWorkflowScopeInfo | undefined = undefined,
|
|
94
95
|
> = {
|
|
95
|
-
readonly
|
|
96
|
+
readonly name: Name;
|
|
96
97
|
readonly args: ArgsSchema;
|
|
97
98
|
readonly config?: ConfigSchema;
|
|
98
|
-
readonly isolation?: NornWorkflowIsolation<IsolationMode>;
|
|
99
99
|
readonly gate?: NornWorkflowGate<ArgsSchema> & {
|
|
100
|
-
describe?(context: NornWorkflowContext<ArgsSchema,
|
|
100
|
+
describe?(context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>): MaybePromise<string>;
|
|
101
101
|
};
|
|
102
|
-
execute(context: NornWorkflowContext<ArgsSchema,
|
|
102
|
+
execute(context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>): MaybePromise<WorkflowResult>;
|
|
103
103
|
} & NornWorkflowInstructions;
|
|
104
104
|
|
|
105
105
|
export type NornWorkflowScopeInfo = {
|
|
@@ -107,50 +107,48 @@ export type NornWorkflowScopeInfo = {
|
|
|
107
107
|
readonly config?: TSchema;
|
|
108
108
|
};
|
|
109
109
|
|
|
110
|
-
export type NornWorkflowScope<
|
|
111
|
-
readonly id:
|
|
110
|
+
export type NornWorkflowScope<Name extends string, ConfigSchema extends TSchema | undefined> = {
|
|
111
|
+
readonly id: Name;
|
|
112
112
|
readonly config: ConfigSchema;
|
|
113
|
-
workflow<const
|
|
114
|
-
definition: NornWorkflowDefinition<
|
|
115
|
-
): NornWorkflowDeclaration<`${
|
|
113
|
+
workflow<const LocalName extends string, ArgsSchema extends TSchema, LocalConfig extends TSchema | undefined = undefined>(
|
|
114
|
+
definition: NornWorkflowDefinition<LocalName, ArgsSchema, LocalConfig, { readonly id: Name; readonly config: ConfigSchema }>,
|
|
115
|
+
): NornWorkflowDeclaration<`${Name}.${LocalName}`, ArgsSchema, LocalConfig, { readonly id: Name; readonly config: ConfigSchema }>;
|
|
116
116
|
};
|
|
117
117
|
|
|
118
118
|
export function workflow<
|
|
119
|
-
const
|
|
119
|
+
const Name extends string,
|
|
120
120
|
ArgsSchema extends TSchema,
|
|
121
|
-
const Mode extends NornWorkflowIsolationMode = "runWorkspace",
|
|
122
121
|
ConfigSchema extends TSchema | undefined = undefined,
|
|
123
|
-
>(definition: NornWorkflowDefinition<
|
|
124
|
-
return createWorkflow({ definition, scope: undefined });
|
|
122
|
+
>(definition: NornWorkflowDefinition<Name, ArgsSchema, ConfigSchema>): NornWorkflowDeclaration<Name, ArgsSchema, ConfigSchema, undefined> {
|
|
123
|
+
return createWorkflow({ definition, id: definition.name, scope: undefined });
|
|
125
124
|
}
|
|
126
125
|
|
|
127
|
-
function createWorkflow<Id extends string, ArgsSchema extends TSchema,
|
|
128
|
-
input: { readonly definition: NornWorkflowDefinition<
|
|
129
|
-
): NornWorkflowDeclaration<Id, ArgsSchema,
|
|
130
|
-
const { definition, scope } = input;
|
|
131
|
-
|
|
132
|
-
const callable = (args: StaticEncode<ArgsSchema>): NornRunNext => createWorkflowTransition({ workflowId:
|
|
133
|
-
const result = Object.assign(callable,
|
|
126
|
+
function createWorkflow<Id extends string, ArgsSchema extends TSchema, ConfigSchema extends TSchema | undefined, Scope extends NornWorkflowScopeInfo | undefined>(
|
|
127
|
+
input: { readonly definition: NornWorkflowDefinition<string, ArgsSchema, ConfigSchema, Scope>; readonly id: Id; readonly scope: Scope },
|
|
128
|
+
): NornWorkflowDeclaration<Id, ArgsSchema, ConfigSchema, Scope> {
|
|
129
|
+
const { definition: { name, ...properties }, id, scope } = input;
|
|
130
|
+
assertDeclarationName(name);
|
|
131
|
+
const callable = (args: StaticEncode<ArgsSchema>): NornRunNext => createWorkflowTransition({ workflowId: id, args });
|
|
132
|
+
const result = Object.assign(callable, properties, {
|
|
134
133
|
kind: WORKFLOW_DECLARATION_KIND,
|
|
134
|
+
id,
|
|
135
135
|
scope,
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (!isWorkflowDeclaration(result)) throw new Error(`Invalid workflow definition: ${definition.id}`);
|
|
136
|
+
}) as NornWorkflowDeclaration<Id, ArgsSchema, ConfigSchema, Scope>;
|
|
137
|
+
if (!isWorkflowDeclaration(result)) throw new Error(`Invalid workflow definition: ${id}`);
|
|
139
138
|
assertWorkflowMetadata(result);
|
|
140
139
|
return result;
|
|
141
140
|
}
|
|
142
141
|
|
|
143
|
-
export function workflowScope<const
|
|
144
|
-
input: { readonly
|
|
145
|
-
): NornWorkflowScope<
|
|
146
|
-
|
|
147
|
-
const scope = { id: input.
|
|
142
|
+
export function workflowScope<const Name extends string, ConfigSchema extends TSchema | undefined = undefined>(
|
|
143
|
+
input: { readonly name: Name; readonly config?: ConfigSchema },
|
|
144
|
+
): NornWorkflowScope<Name, ConfigSchema> {
|
|
145
|
+
assertDeclarationName(input.name);
|
|
146
|
+
const scope = { id: input.name, config: input.config as ConfigSchema };
|
|
148
147
|
return {
|
|
149
|
-
id:
|
|
150
|
-
config:
|
|
148
|
+
id: scope.id,
|
|
149
|
+
config: scope.config,
|
|
151
150
|
workflow(definition) {
|
|
152
|
-
|
|
153
|
-
return createWorkflow({ definition: { ...definition, id: `${input.id}.${definition.id}` }, scope });
|
|
151
|
+
return createWorkflow({ definition, id: `${scope.id}.${definition.name}`, scope });
|
|
154
152
|
},
|
|
155
153
|
};
|
|
156
154
|
}
|
|
@@ -163,7 +161,6 @@ export type NornRunNext = {
|
|
|
163
161
|
|
|
164
162
|
export type NornRunOutcomeMetadata = {
|
|
165
163
|
readonly summary?: string;
|
|
166
|
-
readonly artifacts?: Record<string, NornArtifactRef>;
|
|
167
164
|
readonly logs?: Record<string, NornLogRef>;
|
|
168
165
|
readonly data?: Record<string, unknown>;
|
|
169
166
|
};
|
|
@@ -180,10 +177,6 @@ export type NornRunFail = {
|
|
|
180
177
|
|
|
181
178
|
export type WorkflowResult = NornRunNext | NornRunComplete | NornRunFail;
|
|
182
179
|
|
|
183
|
-
export type NornRunFor<TWorkflow extends NornAnyWorkflowDeclaration> = TWorkflow extends { readonly isolation: { readonly mode: "project" } }
|
|
184
|
-
? NornProjectRun
|
|
185
|
-
: NornRun;
|
|
186
|
-
|
|
187
180
|
export type NornRunStartOptions = {
|
|
188
181
|
readonly id?: string;
|
|
189
182
|
readonly name?: string;
|
|
@@ -270,6 +263,7 @@ export type NornRunInfo = {
|
|
|
270
263
|
readonly id: string;
|
|
271
264
|
readonly name: string;
|
|
272
265
|
readonly path: string;
|
|
266
|
+
readonly paths: NornWorkflowPaths;
|
|
273
267
|
readonly entrypointWorkflowId: string;
|
|
274
268
|
readonly currentWorkflowId?: string;
|
|
275
269
|
readonly status: NornRunStatus;
|
|
@@ -298,17 +292,11 @@ export type NornRunCheckpoint = {
|
|
|
298
292
|
export type NornCommandRunInput = {
|
|
299
293
|
readonly label: string;
|
|
300
294
|
readonly command: string | readonly [string, ...string[]];
|
|
301
|
-
readonly cwd
|
|
295
|
+
readonly cwd: string;
|
|
302
296
|
readonly env?: Record<string, string>;
|
|
303
297
|
readonly timeoutMs?: number;
|
|
304
298
|
};
|
|
305
299
|
|
|
306
|
-
export const artifactRefSchema = Type.Object({
|
|
307
|
-
path: Type.String(),
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
export type NornArtifactRef = StaticDecode<typeof artifactRefSchema>;
|
|
311
|
-
|
|
312
300
|
const emptyWorkflowRefArgsSchema = Type.Object({});
|
|
313
301
|
const workflowReferenceInputSchema = Type.Union([
|
|
314
302
|
Type.String({ minLength: 1 }),
|
|
@@ -350,9 +338,9 @@ export type NornAgentBeforeSessionStartContext = {
|
|
|
350
338
|
};
|
|
351
339
|
|
|
352
340
|
export type NornAgentCreateSessionInput = {
|
|
353
|
-
readonly
|
|
341
|
+
readonly customTools?: readonly ToolDefinition[];
|
|
354
342
|
readonly label: string;
|
|
355
|
-
readonly cwd
|
|
343
|
+
readonly cwd: string;
|
|
356
344
|
readonly tools?: string[];
|
|
357
345
|
readonly beforeSessionStart?: (context: NornAgentBeforeSessionStartContext) => MaybePromise<void>;
|
|
358
346
|
readonly model?: CreateAgentSessionOptions["model"];
|
|
@@ -477,36 +465,24 @@ export type NornAgentSession = {
|
|
|
477
465
|
dispose(): Promise<void>;
|
|
478
466
|
};
|
|
479
467
|
|
|
480
|
-
export type NornRun =
|
|
481
|
-
|
|
482
|
-
export type NornProjectRun = NornRunBase & {
|
|
483
|
-
projectRoot: string;
|
|
484
|
-
projectPath(relativePath: string): string;
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
type NornRunBase = {
|
|
468
|
+
export type NornRun = {
|
|
488
469
|
id: string;
|
|
489
|
-
workspace: string;
|
|
490
|
-
cwd: string;
|
|
491
|
-
path(relativePath: string): string;
|
|
492
470
|
next(workflowId: string, args: unknown): NornRunNext;
|
|
493
471
|
complete(metadata?: NornRunOutcomeMetadata): NornRunComplete;
|
|
494
472
|
fail(metadata: NornRunOutcomeMetadata & { readonly summary: string }): NornRunFail;
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
prompt<ResponseSchema extends TSchema>(input: NornAgentSinglePromptInput<ResponseSchema>): Promise<StaticDecode<ResponseSchema>>;
|
|
509
|
-
};
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
export type NornLogs = {
|
|
476
|
+
read(log: NornLogRef): Promise<string>;
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
export type NornCommands = {
|
|
480
|
+
run(input: NornCommandRunInput): Promise<NornCommandRunResult>;
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
export type NornAgents = {
|
|
484
|
+
createSession(input: NornAgentCreateSessionInput): Promise<NornAgentSession>;
|
|
485
|
+
prompt<ResponseSchema extends TSchema>(input: NornAgentSinglePromptInput<ResponseSchema>): Promise<StaticDecode<ResponseSchema>>;
|
|
510
486
|
};
|
|
511
487
|
|
|
512
488
|
export type NornWorkflowSource = {
|
|
@@ -525,7 +501,6 @@ export type NornRegisteredWorkflowInfo = {
|
|
|
525
501
|
readonly id: string;
|
|
526
502
|
readonly instructions?: string;
|
|
527
503
|
readonly isEntrypoint: boolean;
|
|
528
|
-
readonly isolation: NornWorkflowIsolation;
|
|
529
504
|
readonly scope?: { readonly id: string };
|
|
530
505
|
readonly source?: NornWorkflowSource;
|
|
531
506
|
readonly configKey: string;
|
|
@@ -583,13 +558,13 @@ export type NornProjectInfo = {
|
|
|
583
558
|
readonly modules: readonly NornWorkflowSource[];
|
|
584
559
|
};
|
|
585
560
|
|
|
586
|
-
function
|
|
587
|
-
if (typeof
|
|
561
|
+
function assertDeclarationName(name: string): void {
|
|
562
|
+
if (typeof name !== "string" || name.trim().length === 0 || name.includes(".")) throw new Error(`Invalid workflow or scope name: ${name}; expected a nonempty name without dots`);
|
|
588
563
|
}
|
|
589
564
|
|
|
590
565
|
export function isWorkflowDeclaration(value: unknown): value is NornAnyWorkflowDeclaration {
|
|
591
566
|
if (typeof value !== "function") return false;
|
|
592
|
-
const candidate = value as { kind?: unknown; id?: unknown; instructions?: unknown; isEntrypoint?: unknown; args?: unknown; execute?: unknown
|
|
567
|
+
const candidate = value as { kind?: unknown; id?: unknown; instructions?: unknown; isEntrypoint?: unknown; args?: unknown; execute?: unknown };
|
|
593
568
|
return (
|
|
594
569
|
candidate.kind === WORKFLOW_DECLARATION_KIND &&
|
|
595
570
|
typeof candidate.id === "string" &&
|
|
@@ -597,8 +572,7 @@ export function isWorkflowDeclaration(value: unknown): value is NornAnyWorkflowD
|
|
|
597
572
|
(candidate.instructions === undefined || typeof candidate.instructions === "string") &&
|
|
598
573
|
typeof candidate.isEntrypoint === "boolean" &&
|
|
599
574
|
Boolean(candidate.args) &&
|
|
600
|
-
typeof candidate.execute === "function"
|
|
601
|
-
(candidate.isolation?.mode === "runWorkspace" || candidate.isolation?.mode === "project")
|
|
575
|
+
typeof candidate.execute === "function"
|
|
602
576
|
);
|
|
603
577
|
}
|
|
604
578
|
|
|
@@ -1,5 +1,2 @@
|
|
|
1
1
|
export type { CreateAgentSessionOptions, EventBus, PromptOptions, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
export * from "./api.ts";
|
|
3
|
-
export { NornFileCoordinator } from "./files.ts";
|
|
4
|
-
export * from "./resources.ts";
|
|
5
|
-
export * from "./agent-resource-adapter.ts";
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { workflowScope, isWorkflowDeclaration, workflowRefSchema, type NornWorkflowRefSchemaOptions } from "@vimhead.dev/norn";
|
|
2
2
|
import { inspectSchema } from "@vimhead.dev/norn/schema";
|
|
3
3
|
import assert from "node:assert/strict";
|
|
4
4
|
import { Type } from "typebox";
|
|
5
5
|
import { Value } from "typebox/value";
|
|
6
6
|
import { expect, expectTypeOf, test } from "vitest";
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
const refs = workflowScope({ name: "refs" });
|
|
9
|
+
const target = refs.workflow({
|
|
10
|
+
name: "finish", isEntrypoint: false, args: Type.Object({ report: Type.String() }),
|
|
10
11
|
execute() { throw new Error("A callable must select a transition, not execute it"); },
|
|
11
12
|
});
|
|
12
13
|
|
|
@@ -15,7 +16,6 @@ test("callable declarations retain metadata and construct transitions without ex
|
|
|
15
16
|
assert.equal(isWorkflowDeclaration(target), true);
|
|
16
17
|
assert.equal(isWorkflowDeclaration(() => undefined), false);
|
|
17
18
|
assert.equal(Value.Check(target.args, { report: "done" }), true);
|
|
18
|
-
assert.deepEqual(target.isolation, { mode: "runWorkspace" });
|
|
19
19
|
assert.deepEqual(target({ report: "done" }), { type: "next", workflowId: target.id, args: { report: "done" } });
|
|
20
20
|
});
|
|
21
21
|
|
|
@@ -57,7 +57,7 @@ test("optional reference properties remain optional in validation and inspection
|
|
|
57
57
|
test("inspection advertises encoded contributions and invocation does not decode them", () => {
|
|
58
58
|
let decodes = 0;
|
|
59
59
|
const contributions = Type.Object({
|
|
60
|
-
records:
|
|
60
|
+
records: Type.String(),
|
|
61
61
|
count: Type.Decode(Type.String(), value => { decodes++; return Number(value); }),
|
|
62
62
|
label: Type.Optional(Type.String({ default: "collected" })),
|
|
63
63
|
});
|
|
@@ -72,10 +72,10 @@ test("inspection advertises encoded contributions and invocation does not decode
|
|
|
72
72
|
expect(inspected).toHaveProperty("properties.next.x-norn-workflow-ref.contributedArgsSchema.properties.count.type", "string");
|
|
73
73
|
const forwardArgs = { taskId: "task-42", nested: { labels: ["one", "two"], enabled: false, absent: null } };
|
|
74
74
|
const next = Value.Decode(schema, { next: { workflow: "refs.finish", forwardArgs } }).next;
|
|
75
|
-
const contribution = { records:
|
|
75
|
+
const contribution = { records: "records.json", count: "2" };
|
|
76
76
|
assert.deepEqual(next(contribution).args, { ...forwardArgs, ...contribution });
|
|
77
77
|
assert.equal(decodes, 0);
|
|
78
|
-
assert.throws(() => Reflect.apply(next, undefined, [{ records:
|
|
78
|
+
assert.throws(() => Reflect.apply(next, undefined, [{ records: "records.json", count: 2 }]));
|
|
79
79
|
});
|
|
80
80
|
|
|
81
81
|
test("references without contributions advertise an empty object schema", () => {
|
|
@@ -100,10 +100,10 @@ test("optional, nullable and array references retain annotations and callable co
|
|
|
100
100
|
|
|
101
101
|
test("nested references retain encoded payloads in contributed parameters", () => {
|
|
102
102
|
const finalContributions = Type.Object({ summary: Type.String() });
|
|
103
|
-
const contributions = Type.Object({ records:
|
|
103
|
+
const contributions = Type.Object({ records: Type.String(), next: workflowRefSchema({ args: finalContributions }) });
|
|
104
104
|
const reference = workflowRefSchema({ args: contributions });
|
|
105
105
|
expect(inspectSchema(reference)).toHaveProperty("x-norn-workflow-ref.contributedArgsSchema.properties.next.x-norn-workflow-ref.contributedArgsSchema", inspectSchema(finalContributions));
|
|
106
|
-
const args = { records:
|
|
106
|
+
const args = { records: "records.json", next: { workflow: "refs.finish", forwardArgs: { batch: 1 } } };
|
|
107
107
|
const transition = Value.Decode(reference, "intermediate")(args);
|
|
108
108
|
assert.deepEqual(transition.args, args);
|
|
109
109
|
assert.deepEqual(Value.Decode(contributions, transition.args).next({ summary: "done" }).args, { batch: 1, summary: "done" });
|
package/dist/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ import { Value } from "typebox/value";
|
|
|
18
18
|
import { NORN_BUILD_INFO } from "./build-info.js";
|
|
19
19
|
import { renderNornDocumentationIntro, resolveDocumentationCacheRoot, resolveNornDocumentation } from "./documentation.js";
|
|
20
20
|
import { NornEngine } from "./internal/engine.js";
|
|
21
|
+
import { resolveNornAgentDirectory } from "./internal/agent-directory.js";
|
|
21
22
|
import { errorMessage, NornProjectLoadError, NornRunStoppedError } from "./internal/errors.js";
|
|
22
23
|
import { clearRunResumeRequest, readRunLaunchRequest, readRunResumeRequest, writeRunLaunchRequest, writeRunResumeRequest } from "./internal/launch-request.js";
|
|
23
24
|
import { readRunMetrics } from "./internal/metrics.js";
|
|
@@ -25,6 +26,7 @@ import { getRunLeaseOwner, NornRunLease } from "./internal/run-lease.js";
|
|
|
25
26
|
import { generateRunName } from "./internal/run-names.js";
|
|
26
27
|
import { assertRunVersion, getRunInfo, listRuns, mergeInterruptedWorkflowArgs, resolveRunRoot } from "./internal/run-state.js";
|
|
27
28
|
import { NornRunStore } from "./internal/run-store.js";
|
|
29
|
+
import { prepareRunWorkerDirectory } from "./internal/worker-directory.js";
|
|
28
30
|
import { discoverNornProject, findNornProject, inspectNornWorkflow, loadNornProject, NORN_PROJECT_FILE_NAME } from "./workflow-loader.js";
|
|
29
31
|
var RUNS_ROOT = join(".norn", "runs");
|
|
30
32
|
var RUN_WAIT_INTERVAL_MS = 1e3;
|
|
@@ -715,7 +717,7 @@ async function startRun(workflowId, commandArgs) {
|
|
|
715
717
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
716
718
|
await writeRunLaunchRequest(runRoot, { version: 2, type: "run", id, name, workflowId, args, configOverride, createdAt });
|
|
717
719
|
await startDetachedExecuteRun(id, project.projectRoot);
|
|
718
|
-
writeJson({ run: startedRunInfo({ id, name, workflow, runRoot, createdAt }) });
|
|
720
|
+
writeJson({ run: startedRunInfo({ id, name, workflow, runRoot, projectRoot: project.projectRoot, createdAt }) });
|
|
719
721
|
}
|
|
720
722
|
async function resumeRun(run, args) {
|
|
721
723
|
assertNoStructuredInputArgs("runs resume", args);
|
|
@@ -822,8 +824,10 @@ async function executeRun(runId) {
|
|
|
822
824
|
const runRoot = resolve(location.projectRoot, RUNS_ROOT, runId);
|
|
823
825
|
const request = await readOptionalRunLaunchRequest(runRoot) ?? await readRunResumeRequest(runRoot);
|
|
824
826
|
try {
|
|
825
|
-
const
|
|
826
|
-
|
|
827
|
+
const agentDir = resolveNornAgentDirectory({ home: homedir(), environment: process.env });
|
|
828
|
+
process.chdir(await prepareRunWorkerDirectory(runRoot));
|
|
829
|
+
const project = await loadNornProject(location.projectRoot);
|
|
830
|
+
const engine = new NornEngine({ cwd: project.projectRoot, agentDir, signal: abortController.signal, gateMode: "pause", config: project.projectConfig });
|
|
827
831
|
engine.registerWorkflows(project.definitions);
|
|
828
832
|
if (request.type === "run") {
|
|
829
833
|
const workflow = project.registry.workflowById(request.workflowId);
|
|
@@ -918,6 +922,7 @@ function startedRunInfo(input) {
|
|
|
918
922
|
id: input.id,
|
|
919
923
|
name: input.name,
|
|
920
924
|
path: input.runRoot,
|
|
925
|
+
paths: { project: input.projectRoot, workspace: join(input.runRoot, "current", "workspace") },
|
|
921
926
|
entrypointWorkflowId: input.workflow.id,
|
|
922
927
|
currentWorkflowId: input.workflow.id,
|
|
923
928
|
status: "running",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const NORN_GENERATED_BUILD_INFO: {
|
|
2
2
|
readonly kind: "npm-registry";
|
|
3
|
-
readonly version: "0.1.0-tip.
|
|
4
|
-
readonly commit: "
|
|
3
|
+
readonly version: "0.1.0-tip.35568631545.1";
|
|
4
|
+
readonly commit: "f16f4b16ede3d325d56cdbd1f21193b3a3d45d38";
|
|
5
5
|
readonly packageSpec: "@vimhead.dev/norn-cli@tip";
|
|
6
6
|
readonly upgrade: {
|
|
7
7
|
readonly supported: false;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// src/generated-build-info.ts
|
|
2
2
|
var NORN_GENERATED_BUILD_INFO = {
|
|
3
3
|
"kind": "npm-registry",
|
|
4
|
-
"version": "0.1.0-tip.
|
|
5
|
-
"commit": "
|
|
4
|
+
"version": "0.1.0-tip.35568631545.1",
|
|
5
|
+
"commit": "f16f4b16ede3d325d56cdbd1f21193b3a3d45d38",
|
|
6
6
|
"packageSpec": "@vimhead.dev/norn-cli@tip",
|
|
7
7
|
"upgrade": {
|
|
8
8
|
"supported": false,
|
|
@@ -7,9 +7,6 @@ import type { NornRunLogger } from "./run-log.ts";
|
|
|
7
7
|
type NornAgentRunnerInput = {
|
|
8
8
|
readonly id: string;
|
|
9
9
|
readonly runRoot: string;
|
|
10
|
-
readonly boundaryRoot: string;
|
|
11
|
-
readonly boundaryName: string;
|
|
12
|
-
readonly cwd: string;
|
|
13
10
|
readonly signal?: AbortSignal;
|
|
14
11
|
readonly model?: CreateAgentSessionOptions["model"];
|
|
15
12
|
readonly thinkingLevel?: CreateAgentSessionOptions["thinkingLevel"];
|
|
@@ -24,6 +21,5 @@ export declare class NornAgentRunner {
|
|
|
24
21
|
constructor(input: NornAgentRunnerInput);
|
|
25
22
|
createSession(agentInput: NornAgentCreateSessionInput): Promise<NornAgentSession>;
|
|
26
23
|
prompt<ResponseSchema extends TSchema>(agentInput: NornAgentSinglePromptInput<ResponseSchema>): Promise<StaticDecode<ResponseSchema>>;
|
|
27
|
-
private resolveFromCwd;
|
|
28
24
|
}
|
|
29
25
|
export {};
|
package/dist/internal/agents.js
CHANGED
|
@@ -14,17 +14,16 @@ import { inspectSchema } from "@vimhead.dev/norn/schema";
|
|
|
14
14
|
import { randomUUID } from "node:crypto";
|
|
15
15
|
import { mkdir } from "node:fs/promises";
|
|
16
16
|
import { homedir } from "node:os";
|
|
17
|
-
import {
|
|
17
|
+
import { resolve } from "node:path";
|
|
18
|
+
import { requireAbsoluteWorkingDirectory } from "./working-directory.js";
|
|
18
19
|
import { resolveNornAgentDirectory } from "./agent-directory.js";
|
|
19
20
|
import {
|
|
20
21
|
NornAgentResponseToolFactory
|
|
21
22
|
} from "./agent-response-tool.js";
|
|
22
23
|
import { errorMessage } from "./errors.js";
|
|
23
24
|
import { safeFileName } from "./file-names.js";
|
|
24
|
-
import { NornSessionResourceBindings } from "./resource-bindings.js";
|
|
25
25
|
import { agentUsageFromValue, emptyAgentUsage, totalAgentUsage } from "./usage.js";
|
|
26
26
|
var DEFAULT_AGENT_ATTEMPTS = 3;
|
|
27
|
-
var DEFAULT_AGENT_TOOL_ALLOWLIST = ["read", "bash", "edit", "write", AGENT_RESPONSE_TOOL_NAME];
|
|
28
27
|
var NornAgentRunner = class {
|
|
29
28
|
constructor(input) {
|
|
30
29
|
this.input = input;
|
|
@@ -33,7 +32,7 @@ var NornAgentRunner = class {
|
|
|
33
32
|
input;
|
|
34
33
|
responseToolFactory;
|
|
35
34
|
async createSession(agentInput) {
|
|
36
|
-
const cwd =
|
|
35
|
+
const cwd = requireAbsoluteWorkingDirectory(agentInput.cwd);
|
|
37
36
|
const sessionDir = resolve(this.input.runRoot, "sessions");
|
|
38
37
|
await mkdir(sessionDir, { recursive: true });
|
|
39
38
|
const eventBus = createEventBus();
|
|
@@ -53,31 +52,29 @@ var NornAgentRunner = class {
|
|
|
53
52
|
...loader.getExtensions().errors.map((error) => `Failed to load extension ${error.path}: ${error.error}`)
|
|
54
53
|
];
|
|
55
54
|
if (errors.length > 0) throw new Error(errors.join("\n"));
|
|
56
|
-
const
|
|
55
|
+
const customTools = agentInput.customTools ?? [];
|
|
56
|
+
assertCustomToolNames({
|
|
57
|
+
tools: customTools,
|
|
58
|
+
reservedNames: [
|
|
59
|
+
"read",
|
|
60
|
+
"bash",
|
|
61
|
+
"edit",
|
|
62
|
+
"write",
|
|
63
|
+
"grep",
|
|
64
|
+
"find",
|
|
65
|
+
"ls",
|
|
66
|
+
"powershell",
|
|
67
|
+
AGENT_RESPONSE_TOOL_NAME,
|
|
68
|
+
...loader.getExtensions().extensions.flatMap((extension) => [...extension.tools.keys()])
|
|
69
|
+
]
|
|
70
|
+
});
|
|
57
71
|
let session;
|
|
58
72
|
try {
|
|
59
|
-
await resourceBindings.bind({
|
|
60
|
-
adapters: agentInput.resourceAdapters ?? [],
|
|
61
|
-
runId: this.input.id,
|
|
62
|
-
label: agentInput.label,
|
|
63
|
-
reservedTools: [
|
|
64
|
-
"read",
|
|
65
|
-
"bash",
|
|
66
|
-
"edit",
|
|
67
|
-
"write",
|
|
68
|
-
"grep",
|
|
69
|
-
"find",
|
|
70
|
-
"ls",
|
|
71
|
-
"powershell",
|
|
72
|
-
AGENT_RESPONSE_TOOL_NAME,
|
|
73
|
-
...loader.getExtensions().extensions.flatMap((extension) => [...extension.tools.keys()])
|
|
74
|
-
]
|
|
75
|
-
});
|
|
76
73
|
({ session } = await createAgentSessionFromServices({
|
|
77
74
|
services,
|
|
78
75
|
sessionManager: SessionManager.create(cwd, sessionDir),
|
|
79
|
-
tools:
|
|
80
|
-
customTools: [this.responseToolFactory.create(), ...
|
|
76
|
+
tools: withAgentResponseTool(agentInput.tools),
|
|
77
|
+
customTools: [this.responseToolFactory.create(), ...customTools],
|
|
81
78
|
model: agentInput.model ?? this.input.model,
|
|
82
79
|
thinkingLevel: agentInput.thinkingLevel ?? this.input.thinkingLevel
|
|
83
80
|
}));
|
|
@@ -89,7 +86,6 @@ var NornAgentRunner = class {
|
|
|
89
86
|
label: agentInput.label,
|
|
90
87
|
cwd,
|
|
91
88
|
session,
|
|
92
|
-
resourceBindings,
|
|
93
89
|
events: eventBus
|
|
94
90
|
});
|
|
95
91
|
} catch (error) {
|
|
@@ -99,12 +95,7 @@ var NornAgentRunner = class {
|
|
|
99
95
|
} catch (cleanupError) {
|
|
100
96
|
errors2.push(cleanupError);
|
|
101
97
|
}
|
|
102
|
-
|
|
103
|
-
await resourceBindings.dispose();
|
|
104
|
-
} catch (cleanupError) {
|
|
105
|
-
errors2.push(cleanupError);
|
|
106
|
-
}
|
|
107
|
-
if (errors2.length > 1) throw new AggregateError(errors2, "Agent creation and resource cleanup failed");
|
|
98
|
+
if (errors2.length > 1) throw new AggregateError(errors2, "Agent creation and cleanup failed");
|
|
108
99
|
throw error;
|
|
109
100
|
}
|
|
110
101
|
}
|
|
@@ -116,14 +107,6 @@ var NornAgentRunner = class {
|
|
|
116
107
|
await agent.dispose();
|
|
117
108
|
}
|
|
118
109
|
}
|
|
119
|
-
resolveFromCwd(path) {
|
|
120
|
-
const resolvedPath = isAbsolute(path) ? path : resolve(this.input.cwd, path);
|
|
121
|
-
const pathFromBoundary = relative(this.input.boundaryRoot, resolvedPath);
|
|
122
|
-
if (pathFromBoundary === ".." || pathFromBoundary.startsWith(`..${sep}`) || isAbsolute(pathFromBoundary)) {
|
|
123
|
-
throw new Error(`Agent cwd escapes ${this.input.boundaryName} isolation: ${path}`);
|
|
124
|
-
}
|
|
125
|
-
return resolvedPath;
|
|
126
|
-
}
|
|
127
110
|
};
|
|
128
111
|
function systemPromptOverride(agentInput) {
|
|
129
112
|
const systemPrompt = agentInput.systemPrompt;
|
|
@@ -223,11 +206,6 @@ var CreatedNornAgentSession = class {
|
|
|
223
206
|
} catch (error) {
|
|
224
207
|
errors.push(error);
|
|
225
208
|
}
|
|
226
|
-
try {
|
|
227
|
-
await this.input.resourceBindings.dispose();
|
|
228
|
-
} catch (error) {
|
|
229
|
-
errors.push(error);
|
|
230
|
-
}
|
|
231
209
|
if (errors.length) throw new AggregateError(errors, "Agent disposal failed");
|
|
232
210
|
await this.input.logger.record({ type: "agent.disposed", label: this.label });
|
|
233
211
|
}
|
|
@@ -328,8 +306,14 @@ function withResponseToolFinalizationInstruction(responseSchema, label, response
|
|
|
328
306
|
].join("\n");
|
|
329
307
|
}
|
|
330
308
|
function withAgentResponseTool(tools) {
|
|
331
|
-
|
|
332
|
-
|
|
309
|
+
return tools === void 0 ? void 0 : Array.from(/* @__PURE__ */ new Set([...tools, AGENT_RESPONSE_TOOL_NAME]));
|
|
310
|
+
}
|
|
311
|
+
function assertCustomToolNames(input) {
|
|
312
|
+
const names = new Set(input.reservedNames);
|
|
313
|
+
for (const tool of input.tools) {
|
|
314
|
+
if (names.has(tool.name)) throw new Error(`Custom tool name collision: ${tool.name}`);
|
|
315
|
+
names.add(tool.name);
|
|
316
|
+
}
|
|
333
317
|
}
|
|
334
318
|
export {
|
|
335
319
|
NornAgentRunner
|
|
@@ -2,9 +2,6 @@ import type { NornCommandRunInput, NornCommandRunResult } from "@vimhead.dev/nor
|
|
|
2
2
|
import type { NornRunLogs } from "./logs.ts";
|
|
3
3
|
import type { NornRunLogger } from "./run-log.ts";
|
|
4
4
|
type NornCommandRunnerInput = {
|
|
5
|
-
readonly boundaryRoot: string;
|
|
6
|
-
readonly boundaryName: string;
|
|
7
|
-
readonly cwd: string;
|
|
8
5
|
readonly signal?: AbortSignal;
|
|
9
6
|
readonly logs: NornRunLogs;
|
|
10
7
|
readonly logger: NornRunLogger;
|
|
@@ -13,6 +10,5 @@ export declare class NornCommandRunner {
|
|
|
13
10
|
private readonly input;
|
|
14
11
|
constructor(input: NornCommandRunnerInput);
|
|
15
12
|
run(commandInput: NornCommandRunInput): Promise<NornCommandRunResult>;
|
|
16
|
-
private resolveFromCwd;
|
|
17
13
|
}
|
|
18
14
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/internal/commands.ts
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
|
-
import {
|
|
4
|
+
import { requireAbsoluteWorkingDirectory } from "./working-directory.js";
|
|
5
5
|
import { errorMessage } from "./errors.js";
|
|
6
6
|
import { safeFileName } from "./file-names.js";
|
|
7
7
|
var NornCommandRunner = class {
|
|
@@ -10,7 +10,7 @@ var NornCommandRunner = class {
|
|
|
10
10
|
}
|
|
11
11
|
input;
|
|
12
12
|
async run(commandInput) {
|
|
13
|
-
const cwd =
|
|
13
|
+
const cwd = requireAbsoluteWorkingDirectory(commandInput.cwd);
|
|
14
14
|
const startedAtMs = Date.now();
|
|
15
15
|
const invocationId = randomUUID();
|
|
16
16
|
const stdoutRef = commandLog({ label: commandInput.label, invocationId, stream: "stdout" });
|
|
@@ -56,14 +56,6 @@ var NornCommandRunner = class {
|
|
|
56
56
|
});
|
|
57
57
|
return commandResult;
|
|
58
58
|
}
|
|
59
|
-
resolveFromCwd(path) {
|
|
60
|
-
const resolvedPath = isAbsolute(path) ? path : resolve(this.input.cwd, path);
|
|
61
|
-
const pathFromBoundary = relative(this.input.boundaryRoot, resolvedPath);
|
|
62
|
-
if (pathFromBoundary === ".." || pathFromBoundary.startsWith(`..${sep}`) || isAbsolute(pathFromBoundary)) {
|
|
63
|
-
throw new Error(`Command cwd escapes ${this.input.boundaryName} isolation: ${path}`);
|
|
64
|
-
}
|
|
65
|
-
return resolvedPath;
|
|
66
|
-
}
|
|
67
59
|
};
|
|
68
60
|
async function spawnCommand(input) {
|
|
69
61
|
const command = typeof input.command === "string" ? "bash" : input.command[0];
|