@vimhead.dev/norn 0.1.0-tip.35359392805.1 → 0.1.0-tip.35436871363.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/dist/api.d.ts +98 -179
- package/dist/api.js +40 -81
- package/dist/index.d.ts +1 -4
- package/dist/index.js +0 -7
- package/dist/schema.js +3 -3
- package/package.json +1 -5
- package/dist/agent-resource-adapter.d.ts +0 -12
- package/dist/agent-resource-adapter.js +0 -0
- package/dist/files.d.ts +0 -17
- package/dist/files.js +0 -162
- package/dist/resources.d.ts +0 -17
- package/dist/resources.js +0 -0
- package/dist/state-adapter.d.ts +0 -10
- package/dist/state-adapter.js +0 -70
package/dist/api.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { CreateAgentSessionOptions, EventBus, PromptOptions } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { Type, type
|
|
1
|
+
import type { CreateAgentSessionOptions, EventBus, PromptOptions, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Type, type StaticDecode, type StaticEncode, type TCodec, type TSchema } from "typebox";
|
|
3
3
|
import type { TLocalizedValidationError } from "typebox/error";
|
|
4
4
|
declare const WORKFLOW_DECLARATION_KIND = "norn.workflow";
|
|
5
5
|
export type MaybePromise<T> = T | Promise<T>;
|
|
@@ -8,31 +8,37 @@ export type NornWorkflowAnyGate = {
|
|
|
8
8
|
readonly enabled: true;
|
|
9
9
|
readonly fields?: readonly string[];
|
|
10
10
|
};
|
|
11
|
-
export type
|
|
12
|
-
|
|
13
|
-
readonly
|
|
11
|
+
export type NornWorkflowPaths = {
|
|
12
|
+
/** Absolute project root. Excluded from run checkpoints and rollback. */
|
|
13
|
+
readonly project: string;
|
|
14
|
+
/** Absolute, initially empty workspace. Saved in run checkpoints and restored on rollback. */
|
|
15
|
+
readonly workspace: string;
|
|
14
16
|
};
|
|
15
|
-
export type NornWorkflowDeclaration<Id extends string = string,
|
|
16
|
-
(
|
|
17
|
+
export type NornWorkflowDeclaration<Id extends string = string, ArgsSchema extends TSchema = TSchema, ConfigSchema extends TSchema | undefined = TSchema | undefined, Scope extends NornWorkflowScopeInfo | undefined = NornWorkflowScopeInfo | undefined> = {
|
|
18
|
+
(args: StaticEncode<ArgsSchema>): NornRunNext;
|
|
17
19
|
readonly kind: typeof WORKFLOW_DECLARATION_KIND;
|
|
18
20
|
readonly id: Id;
|
|
19
21
|
readonly isEntrypoint: boolean;
|
|
20
22
|
readonly instructions?: string;
|
|
21
|
-
readonly
|
|
22
|
-
readonly gate?:
|
|
23
|
-
|
|
23
|
+
readonly args: ArgsSchema;
|
|
24
|
+
readonly gate?: NornWorkflowGate<ArgsSchema> & {
|
|
25
|
+
describe?(context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>): MaybePromise<string>;
|
|
26
|
+
};
|
|
27
|
+
readonly config?: ConfigSchema;
|
|
28
|
+
readonly scope: Scope;
|
|
29
|
+
execute(context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>): MaybePromise<WorkflowResult>;
|
|
24
30
|
};
|
|
25
|
-
export type NornAnyWorkflowDeclaration = Pick<NornWorkflowDeclaration, keyof NornWorkflowDeclaration> & ((
|
|
26
|
-
export type NornWorkflowRefSchemaOptions<
|
|
27
|
-
readonly
|
|
31
|
+
export type NornAnyWorkflowDeclaration = Pick<NornWorkflowDeclaration, keyof NornWorkflowDeclaration> & ((args: never) => NornRunNext);
|
|
32
|
+
export type NornWorkflowRefSchemaOptions<ArgsSchema extends TSchema = TSchema> = {
|
|
33
|
+
readonly args?: ArgsSchema;
|
|
28
34
|
};
|
|
29
35
|
export type NornWorkflowRefInput = StaticEncode<typeof workflowReferenceInputSchema>;
|
|
30
|
-
export type NornWorkflowRefOutput<
|
|
31
|
-
export type
|
|
32
|
-
export type
|
|
33
|
-
export type NornWorkflowGate<
|
|
36
|
+
export type NornWorkflowRefOutput<ArgsSchema extends TSchema> = (args: StaticEncode<ArgsSchema> & object) => NornRunNext;
|
|
37
|
+
export type NornWorkflowArgsInput<TWorkflow extends NornAnyWorkflowDeclaration> = StaticEncode<TWorkflow["args"]>;
|
|
38
|
+
export type NornWorkflowArgs<TWorkflow extends NornAnyWorkflowDeclaration> = StaticDecode<TWorkflow["args"]>;
|
|
39
|
+
export type NornWorkflowGate<ArgsSchema extends TSchema> = unknown extends StaticEncode<ArgsSchema> ? NornWorkflowAnyGate : StaticEncode<ArgsSchema> extends Record<string, unknown> ? {
|
|
34
40
|
readonly enabled: true;
|
|
35
|
-
readonly fields?: readonly Extract<keyof StaticEncode<
|
|
41
|
+
readonly fields?: readonly Extract<keyof StaticEncode<ArgsSchema>, string>[];
|
|
36
42
|
} : {
|
|
37
43
|
readonly enabled: true;
|
|
38
44
|
readonly fields?: never;
|
|
@@ -44,98 +50,54 @@ type NornWorkflowInstructions = {
|
|
|
44
50
|
readonly isEntrypoint: false;
|
|
45
51
|
readonly instructions?: string;
|
|
46
52
|
};
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
readonly
|
|
50
|
-
readonly
|
|
51
|
-
readonly
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
readonly
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
readonly isolation?: NornWorkflowIsolation;
|
|
58
|
-
} & NornWorkflowInstructions;
|
|
59
|
-
export type NornWorkflowStateDefinition<Schema extends TSchema = TSchema, Id extends string = string> = {
|
|
60
|
-
readonly id: Id;
|
|
61
|
-
readonly description?: string;
|
|
62
|
-
readonly schema: Schema;
|
|
63
|
-
};
|
|
64
|
-
export type NornWorkflowStateDefinitionInput<Schema extends TSchema = TSchema, Id extends string | undefined = string | undefined> = {
|
|
65
|
-
readonly id?: Id;
|
|
66
|
-
readonly description?: string;
|
|
67
|
-
readonly schema: Schema;
|
|
68
|
-
};
|
|
69
|
-
export type NornWorkflowPluginWorkflows = Record<string, NornAnyWorkflowDefinition>;
|
|
70
|
-
export type NornWorkflowPluginStateTree = {
|
|
71
|
-
readonly [key: string]: NornWorkflowPluginStateTreeNode;
|
|
72
|
-
};
|
|
73
|
-
type NornStateSchema = TSchema & ({
|
|
74
|
-
readonly "~kind": string;
|
|
75
|
-
} | {
|
|
76
|
-
readonly "~unsafe": unknown;
|
|
77
|
-
});
|
|
78
|
-
export type NornWorkflowPluginStateTreeNode = NornStateSchema | NornWorkflowStateDefinitionInput | NornWorkflowPluginStateTree;
|
|
79
|
-
type JoinPath<Head extends string, Parts extends readonly string[]> = Parts extends readonly [] ? Head : Parts extends readonly [infer First extends string, ...infer Rest extends string[]] ? JoinPath<`${Head}.${First}`, Rest> : string;
|
|
80
|
-
type NornInvalidGateFields<TWorkflow> = TWorkflow extends {
|
|
81
|
-
readonly params: infer ParamsSchema extends TSchema;
|
|
82
|
-
readonly gate: {
|
|
83
|
-
readonly fields: infer Fields extends readonly string[];
|
|
53
|
+
type WorkflowConfig<Schema extends TSchema | undefined> = Schema extends TSchema ? StaticDecode<Schema> : undefined;
|
|
54
|
+
export type NornWorkflowContext<ArgsSchema extends TSchema = TSchema, ConfigSchema extends TSchema | undefined = TSchema | undefined, Scope extends NornWorkflowScopeInfo | undefined = NornWorkflowScopeInfo | undefined> = {
|
|
55
|
+
readonly args: StaticDecode<ArgsSchema>;
|
|
56
|
+
readonly config: WorkflowConfig<ConfigSchema>;
|
|
57
|
+
readonly paths: NornWorkflowPaths;
|
|
58
|
+
readonly run: NornRun;
|
|
59
|
+
} & (Scope extends NornWorkflowScopeInfo ? {
|
|
60
|
+
readonly scope: {
|
|
61
|
+
readonly id: Scope["id"];
|
|
62
|
+
readonly config: WorkflowConfig<Scope["config"]>;
|
|
84
63
|
};
|
|
85
|
-
}
|
|
86
|
-
type
|
|
87
|
-
readonly
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
};
|
|
93
|
-
export type NornValidatedWorkflowGates<Workflows> = {
|
|
94
|
-
readonly [Key in keyof Workflows]: NornValidatedWorkflowGate<Workflows[Key]>;
|
|
95
|
-
};
|
|
96
|
-
export type NornQualifiedPluginWorkflow<PluginId extends string, WorkflowKey extends string, TWorkflow extends NornAnyWorkflowDefinition> = TWorkflow extends {
|
|
97
|
-
readonly params: infer ParamsSchema extends TSchema;
|
|
98
|
-
} ? NornWorkflowDeclaration<TWorkflow extends {
|
|
99
|
-
readonly id: infer ExplicitId extends string;
|
|
100
|
-
} ? ExplicitId : `${PluginId}.${WorkflowKey}`, ParamsSchema, TWorkflow extends {
|
|
101
|
-
readonly isolation: {
|
|
102
|
-
readonly mode: infer IsolationMode extends NornWorkflowIsolationMode;
|
|
64
|
+
} : {});
|
|
65
|
+
export type NornWorkflowDefinition<Id extends string = string, ArgsSchema extends TSchema = TSchema, ConfigSchema extends TSchema | undefined = undefined, Scope extends NornWorkflowScopeInfo | undefined = undefined> = {
|
|
66
|
+
readonly id: Id;
|
|
67
|
+
readonly args: ArgsSchema;
|
|
68
|
+
readonly config?: ConfigSchema;
|
|
69
|
+
readonly gate?: NornWorkflowGate<ArgsSchema> & {
|
|
70
|
+
describe?(context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>): MaybePromise<string>;
|
|
103
71
|
};
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
readonly [Key in keyof States]: States[Key] extends NornStateSchema ? NornWorkflowStateDefinition<States[Key], JoinPath<PluginId, [...Path, Key & string]>> : States[Key] extends NornWorkflowStateDefinitionInput<infer Schema extends NornStateSchema> ? NornWorkflowStateDefinition<Schema, States[Key] extends {
|
|
110
|
-
readonly id: infer Id extends string;
|
|
111
|
-
} ? Id : JoinPath<PluginId, [...Path, Key & string]>> : States[Key] extends NornWorkflowPluginStateTree ? NornQualifiedPluginStates<PluginId, States[Key], [...Path, Key & string]> : never;
|
|
72
|
+
execute(context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>): MaybePromise<WorkflowResult>;
|
|
73
|
+
} & NornWorkflowInstructions;
|
|
74
|
+
export type NornWorkflowScopeInfo = {
|
|
75
|
+
readonly id: string;
|
|
76
|
+
readonly config?: TSchema;
|
|
112
77
|
};
|
|
113
|
-
export type
|
|
114
|
-
readonly id:
|
|
115
|
-
readonly config
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export
|
|
125
|
-
|
|
78
|
+
export type NornWorkflowScope<Id extends string, ConfigSchema extends TSchema | undefined> = {
|
|
79
|
+
readonly id: Id;
|
|
80
|
+
readonly config: ConfigSchema;
|
|
81
|
+
workflow<const LocalId extends string, ArgsSchema extends TSchema, LocalConfig extends TSchema | undefined = undefined>(definition: NornWorkflowDefinition<LocalId, ArgsSchema, LocalConfig, {
|
|
82
|
+
readonly id: Id;
|
|
83
|
+
readonly config: ConfigSchema;
|
|
84
|
+
}>): NornWorkflowDeclaration<`${Id}.${LocalId}`, ArgsSchema, LocalConfig, {
|
|
85
|
+
readonly id: Id;
|
|
86
|
+
readonly config: ConfigSchema;
|
|
87
|
+
}>;
|
|
88
|
+
};
|
|
89
|
+
export declare function workflow<const Id extends string, ArgsSchema extends TSchema, ConfigSchema extends TSchema | undefined = undefined>(definition: NornWorkflowDefinition<Id, ArgsSchema, ConfigSchema>): NornWorkflowDeclaration<Id, ArgsSchema, ConfigSchema, undefined>;
|
|
90
|
+
export declare function workflowScope<const Id extends string, ConfigSchema extends TSchema | undefined = undefined>(input: {
|
|
91
|
+
readonly id: Id;
|
|
126
92
|
readonly config?: ConfigSchema;
|
|
127
|
-
|
|
128
|
-
readonly states?: States;
|
|
129
|
-
};
|
|
130
|
-
export declare function definePluginManifest<const PluginId extends string, const ConfigSchema extends TSchema | undefined = undefined, const Workflows extends NornWorkflowPluginWorkflows = NornWorkflowPluginWorkflows, const States extends NornWorkflowPluginStateTree | undefined = undefined>(input: NornDefinePluginManifestInput<PluginId, ConfigSchema, Workflows, States>): NornWorkflowPluginManifest<PluginId, ConfigSchema, NornQualifiedPluginWorkflows<PluginId, Workflows>, States extends NornWorkflowPluginStateTree ? NornQualifiedPluginStates<PluginId, States> : undefined>;
|
|
93
|
+
}): NornWorkflowScope<Id, ConfigSchema>;
|
|
131
94
|
export type NornRunNext = {
|
|
132
95
|
readonly type: "next";
|
|
133
96
|
readonly workflowId: string;
|
|
134
|
-
readonly
|
|
97
|
+
readonly args: unknown;
|
|
135
98
|
};
|
|
136
99
|
export type NornRunOutcomeMetadata = {
|
|
137
100
|
readonly summary?: string;
|
|
138
|
-
readonly artifacts?: Record<string, NornArtifactRef>;
|
|
139
101
|
readonly logs?: Record<string, NornLogRef>;
|
|
140
102
|
readonly data?: Record<string, unknown>;
|
|
141
103
|
};
|
|
@@ -149,19 +111,7 @@ export type NornRunFail = {
|
|
|
149
111
|
readonly summary: string;
|
|
150
112
|
};
|
|
151
113
|
};
|
|
152
|
-
export type
|
|
153
|
-
export type NornRunFor<TWorkflow extends NornAnyWorkflowDeclaration> = TWorkflow extends {
|
|
154
|
-
readonly isolation: {
|
|
155
|
-
readonly mode: "project";
|
|
156
|
-
};
|
|
157
|
-
} ? NornProjectRun : NornRun;
|
|
158
|
-
export type NornWorkflowGateImplementation<TWorkflow extends NornAnyWorkflowDeclaration, TConfig> = {
|
|
159
|
-
describe(run: NornRunFor<TWorkflow>, params: NornWorkflowParams<TWorkflow>, config: TConfig): MaybePromise<string>;
|
|
160
|
-
};
|
|
161
|
-
export type NornWorkflowImplementation<TWorkflow extends NornAnyWorkflowDeclaration, TConfig = unknown> = {
|
|
162
|
-
readonly gate?: NornWorkflowGateImplementation<TWorkflow, TConfig>;
|
|
163
|
-
execute(run: NornRunFor<TWorkflow>, params: NornWorkflowParams<TWorkflow>, config: TConfig): MaybePromise<NornWorkflowExecutionResult>;
|
|
164
|
-
};
|
|
114
|
+
export type WorkflowResult = NornRunNext | NornRunComplete | NornRunFail;
|
|
165
115
|
export type NornRunStartOptions = {
|
|
166
116
|
readonly id?: string;
|
|
167
117
|
readonly name?: string;
|
|
@@ -206,7 +156,7 @@ export type NornStoppedRunResult = {
|
|
|
206
156
|
};
|
|
207
157
|
export type NornRunInterruption = {
|
|
208
158
|
readonly workflowId: string;
|
|
209
|
-
readonly
|
|
159
|
+
readonly args: unknown;
|
|
210
160
|
readonly description: string;
|
|
211
161
|
readonly fields?: readonly string[];
|
|
212
162
|
};
|
|
@@ -239,6 +189,7 @@ export type NornRunInfo = {
|
|
|
239
189
|
readonly id: string;
|
|
240
190
|
readonly name: string;
|
|
241
191
|
readonly path: string;
|
|
192
|
+
readonly paths: NornWorkflowPaths;
|
|
242
193
|
readonly entrypointWorkflowId: string;
|
|
243
194
|
readonly currentWorkflowId?: string;
|
|
244
195
|
readonly status: NornRunStatus;
|
|
@@ -261,46 +212,19 @@ export type NornRunCheckpoint = {
|
|
|
261
212
|
readonly message: string;
|
|
262
213
|
readonly createdAt: string;
|
|
263
214
|
};
|
|
264
|
-
export type NornWorkflowStateReader = {
|
|
265
|
-
get<Schema extends TSchema>(state: NornWorkflowStateDefinition<Schema>): Promise<Static<Schema>>;
|
|
266
|
-
getOptional<Schema extends TSchema>(state: NornWorkflowStateDefinition<Schema>): Promise<Static<Schema> | undefined>;
|
|
267
|
-
};
|
|
268
|
-
export type NornWorkflowState = NornWorkflowStateReader & {
|
|
269
|
-
set<Schema extends TSchema>(state: NornWorkflowStateDefinition<Schema>, value: NoInfer<Static<Schema>>): Promise<void>;
|
|
270
|
-
};
|
|
271
|
-
export type NornWorkflowPluginContext = {
|
|
272
|
-
readonly cwd: string;
|
|
273
|
-
readonly state: NornWorkflowState;
|
|
274
|
-
};
|
|
275
|
-
export type NornWorkflowPluginImplementation<TManifest extends NornAnyWorkflowPluginManifest> = {
|
|
276
|
-
readonly workflows: {
|
|
277
|
-
readonly [Key in keyof TManifest["workflows"]]: NornWorkflowImplementation<TManifest["workflows"][Key], NornWorkflowPluginConfig<TManifest>>;
|
|
278
|
-
};
|
|
279
|
-
};
|
|
280
|
-
export type NornWorkflowPluginImplementationFactory<TManifest extends NornAnyWorkflowPluginManifest> = (context: NornWorkflowPluginContext) => NornWorkflowPluginImplementation<TManifest>;
|
|
281
|
-
export type NornWorkflowPluginImplementationInput<TManifest extends NornAnyWorkflowPluginManifest> = NornWorkflowPluginImplementation<TManifest> | NornWorkflowPluginImplementationFactory<TManifest>;
|
|
282
|
-
export type NornWorkflowPlugin<TManifest extends NornAnyWorkflowPluginManifest = NornAnyWorkflowPluginManifest> = {
|
|
283
|
-
readonly manifest: TManifest;
|
|
284
|
-
readonly implementation: NornWorkflowPluginImplementationInput<TManifest>;
|
|
285
|
-
};
|
|
286
|
-
export declare function definePlugin<TManifest extends NornAnyWorkflowPluginManifest>(manifest: TManifest, implementation: NornWorkflowPluginImplementationInput<TManifest>): NornWorkflowPlugin<TManifest>;
|
|
287
215
|
export type NornCommandRunInput = {
|
|
288
216
|
readonly label: string;
|
|
289
217
|
readonly command: string | readonly [string, ...string[]];
|
|
290
|
-
readonly cwd
|
|
218
|
+
readonly cwd: string;
|
|
291
219
|
readonly env?: Record<string, string>;
|
|
292
220
|
readonly timeoutMs?: number;
|
|
293
221
|
};
|
|
294
|
-
|
|
295
|
-
path: Type.TString;
|
|
296
|
-
}>;
|
|
297
|
-
export type NornArtifactRef = StaticDecode<typeof artifactRefSchema>;
|
|
298
|
-
declare const emptyWorkflowRefParamsSchema: Type.TObject<{}>;
|
|
222
|
+
declare const emptyWorkflowRefArgsSchema: Type.TObject<{}>;
|
|
299
223
|
declare const workflowReferenceInputSchema: Type.TUnion<[Type.TString, Type.TObject<{
|
|
300
224
|
workflow: Type.TString;
|
|
301
|
-
|
|
225
|
+
forwardArgs: Type.TRecord<"^.*$", Type.TUnknown>;
|
|
302
226
|
}>]>;
|
|
303
|
-
export declare function workflowRefSchema<
|
|
227
|
+
export declare function workflowRefSchema<ArgsSchema extends TSchema = typeof emptyWorkflowRefArgsSchema>(options?: NornWorkflowRefSchemaOptions<ArgsSchema>): TCodec<typeof workflowReferenceInputSchema, NornWorkflowRefOutput<ArgsSchema>>;
|
|
304
228
|
export type NornLogRef = {
|
|
305
229
|
readonly id: string;
|
|
306
230
|
};
|
|
@@ -319,9 +243,9 @@ export type NornAgentBeforeSessionStartContext = {
|
|
|
319
243
|
readonly events: EventBus;
|
|
320
244
|
};
|
|
321
245
|
export type NornAgentCreateSessionInput = {
|
|
322
|
-
readonly
|
|
246
|
+
readonly customTools?: readonly ToolDefinition[];
|
|
323
247
|
readonly label: string;
|
|
324
|
-
readonly cwd
|
|
248
|
+
readonly cwd: string;
|
|
325
249
|
readonly tools?: string[];
|
|
326
250
|
readonly beforeSessionStart?: (context: NornAgentBeforeSessionStartContext) => MaybePromise<void>;
|
|
327
251
|
readonly model?: CreateAgentSessionOptions["model"];
|
|
@@ -433,27 +357,13 @@ export type NornAgentSession = {
|
|
|
433
357
|
prompt<ResponseSchema extends TSchema>(input: NornAgentPromptInput<ResponseSchema>): Promise<StaticDecode<ResponseSchema>>;
|
|
434
358
|
dispose(): Promise<void>;
|
|
435
359
|
};
|
|
436
|
-
export type NornRun =
|
|
437
|
-
export type NornProjectRun = NornRunBase & {
|
|
438
|
-
projectRoot: string;
|
|
439
|
-
projectPath(relativePath: string): string;
|
|
440
|
-
};
|
|
441
|
-
type NornRunBase = {
|
|
360
|
+
export type NornRun = {
|
|
442
361
|
id: string;
|
|
443
|
-
|
|
444
|
-
cwd: string;
|
|
445
|
-
path(relativePath: string): string;
|
|
446
|
-
next(workflowId: string, params: unknown): NornRunNext;
|
|
362
|
+
next(workflowId: string, args: unknown): NornRunNext;
|
|
447
363
|
complete(metadata?: NornRunOutcomeMetadata): NornRunComplete;
|
|
448
364
|
fail(metadata: NornRunOutcomeMetadata & {
|
|
449
365
|
readonly summary: string;
|
|
450
366
|
}): NornRunFail;
|
|
451
|
-
resources: import("./resources.ts").NornResources;
|
|
452
|
-
state: NornWorkflowState;
|
|
453
|
-
artifacts: {
|
|
454
|
-
write(path: string, content: string): Promise<NornArtifactRef>;
|
|
455
|
-
read(ref: NornArtifactRef): Promise<string>;
|
|
456
|
-
};
|
|
457
367
|
logs: {
|
|
458
368
|
read(log: NornLogRef): Promise<string>;
|
|
459
369
|
};
|
|
@@ -465,10 +375,9 @@ type NornRunBase = {
|
|
|
465
375
|
prompt<ResponseSchema extends TSchema>(input: NornAgentSinglePromptInput<ResponseSchema>): Promise<StaticDecode<ResponseSchema>>;
|
|
466
376
|
};
|
|
467
377
|
};
|
|
468
|
-
export type
|
|
469
|
-
readonly
|
|
470
|
-
readonly
|
|
471
|
-
readonly configPath?: string;
|
|
378
|
+
export type NornWorkflowSource = {
|
|
379
|
+
readonly path: string;
|
|
380
|
+
readonly configPath: string;
|
|
472
381
|
};
|
|
473
382
|
export type NornJsonSchema = Record<string, unknown>;
|
|
474
383
|
export type NornWorkflowGateInfo = {
|
|
@@ -479,25 +388,34 @@ export type NornRegisteredWorkflowInfo = {
|
|
|
479
388
|
readonly id: string;
|
|
480
389
|
readonly instructions?: string;
|
|
481
390
|
readonly isEntrypoint: boolean;
|
|
482
|
-
readonly
|
|
483
|
-
|
|
391
|
+
readonly scope?: {
|
|
392
|
+
readonly id: string;
|
|
393
|
+
};
|
|
394
|
+
readonly source?: NornWorkflowSource;
|
|
395
|
+
readonly configKey: string;
|
|
484
396
|
};
|
|
485
397
|
export type NornInspectedWorkflowInfo = NornRegisteredWorkflowInfo & {
|
|
486
|
-
readonly
|
|
398
|
+
readonly scope?: {
|
|
399
|
+
readonly id: string;
|
|
400
|
+
readonly configKey: string;
|
|
401
|
+
readonly configSchema: NornJsonSchema | null;
|
|
402
|
+
};
|
|
403
|
+
readonly argsSchema: NornJsonSchema;
|
|
404
|
+
readonly configSchema: NornJsonSchema | null;
|
|
487
405
|
readonly gate: NornWorkflowGateInfo | null;
|
|
488
406
|
};
|
|
489
|
-
export type
|
|
407
|
+
export type NornWorkflowDiagnostic = {
|
|
490
408
|
readonly configPath: string;
|
|
491
|
-
readonly
|
|
492
|
-
readonly
|
|
409
|
+
readonly modulePath: string;
|
|
410
|
+
readonly scopeId: string | null;
|
|
493
411
|
readonly workflowId: string | null;
|
|
494
|
-
readonly stage: "import" | "declaration" | "config" | "
|
|
412
|
+
readonly stage: "import" | "declaration" | "config" | "duplicate" | "schema";
|
|
495
413
|
readonly message: string;
|
|
496
414
|
readonly issues: readonly TLocalizedValidationError[];
|
|
497
415
|
};
|
|
498
416
|
export type NornProjectLoadStatus = {
|
|
499
417
|
readonly isComplete: boolean;
|
|
500
|
-
readonly diagnostics: readonly
|
|
418
|
+
readonly diagnostics: readonly NornWorkflowDiagnostic[];
|
|
501
419
|
};
|
|
502
420
|
export type NornWorkflowCatalogInfo = NornProjectLoadStatus & {
|
|
503
421
|
readonly workflows: readonly NornRegisteredWorkflowInfo[];
|
|
@@ -508,7 +426,9 @@ export type NornWorkflowInspection = NornProjectLoadStatus & {
|
|
|
508
426
|
export type NornProjectInspection = NornProjectLoadStatus & {
|
|
509
427
|
readonly project: NornProjectInfo;
|
|
510
428
|
};
|
|
511
|
-
export type
|
|
429
|
+
export type NornProjectConfigurationInfo = {
|
|
430
|
+
readonly key: string;
|
|
431
|
+
readonly scopeId: string | null;
|
|
512
432
|
readonly configSchema: NornJsonSchema | null;
|
|
513
433
|
readonly config: unknown;
|
|
514
434
|
};
|
|
@@ -519,11 +439,10 @@ export type NornProjectInfo = {
|
|
|
519
439
|
readonly configPath: string;
|
|
520
440
|
readonly configRoot: string;
|
|
521
441
|
readonly configFiles: readonly string[];
|
|
522
|
-
readonly
|
|
442
|
+
readonly configurations: readonly NornProjectConfigurationInfo[];
|
|
443
|
+
readonly modules: readonly NornWorkflowSource[];
|
|
523
444
|
};
|
|
524
445
|
export declare function isWorkflowDeclaration(value: unknown): value is NornAnyWorkflowDeclaration;
|
|
525
|
-
export declare function isWorkflowPlugin(value: unknown): value is NornWorkflowPlugin;
|
|
526
|
-
export declare function isWorkflowPluginManifest(value: unknown): value is NornAnyWorkflowPluginManifest;
|
|
527
446
|
export declare function isWorkflowNext(value: unknown): value is NornRunNext;
|
|
528
447
|
export declare function isWorkflowComplete(value: unknown): value is NornRunComplete;
|
|
529
448
|
export declare function isWorkflowFail(value: unknown): value is NornRunFail;
|
package/dist/api.js
CHANGED
|
@@ -4,103 +4,65 @@ import { Type } from "typebox";
|
|
|
4
4
|
// ../core/src/workflow-transition.ts
|
|
5
5
|
function createWorkflowTransition(input) {
|
|
6
6
|
if (typeof input.workflowId !== "string" || input.workflowId.trim().length === 0) throw new Error("Workflow transition requires a nonempty workflow ID");
|
|
7
|
-
return { type: "next", workflowId: input.workflowId,
|
|
7
|
+
return { type: "next", workflowId: input.workflowId, args: input.args };
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
// src/api.ts
|
|
11
11
|
import { Value } from "typebox/value";
|
|
12
|
-
import { isPlainObject, jsonValueSchema } from "./schema.js";
|
|
12
|
+
import { assertWorkflowMetadata, isPlainObject, jsonValueSchema } from "./schema.js";
|
|
13
13
|
var WORKFLOW_DECLARATION_KIND = "norn.workflow";
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
);
|
|
14
|
+
function workflow(definition) {
|
|
15
|
+
return createWorkflow({ definition, scope: void 0 });
|
|
16
|
+
}
|
|
17
|
+
function createWorkflow(input) {
|
|
18
|
+
const { definition, scope } = input;
|
|
19
|
+
assertDeclarationId(definition.id);
|
|
20
|
+
const callable = (args) => createWorkflowTransition({ workflowId: definition.id, args });
|
|
21
|
+
const result = Object.assign(callable, definition, {
|
|
22
|
+
kind: WORKFLOW_DECLARATION_KIND,
|
|
23
|
+
scope
|
|
24
|
+
});
|
|
25
|
+
if (!isWorkflowDeclaration(result)) throw new Error(`Invalid workflow definition: ${definition.id}`);
|
|
26
|
+
assertWorkflowMetadata(result);
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
function workflowScope(input) {
|
|
30
|
+
assertDeclarationId(input.id);
|
|
31
|
+
const scope = { id: input.id, config: input.config };
|
|
20
32
|
return {
|
|
21
33
|
id: input.id,
|
|
22
34
|
config: input.config,
|
|
23
|
-
|
|
24
|
-
|
|
35
|
+
workflow(definition) {
|
|
36
|
+
assertDeclarationId(definition.id);
|
|
37
|
+
return createWorkflow({ definition: { ...definition, id: `${input.id}.${definition.id}` }, scope });
|
|
38
|
+
}
|
|
25
39
|
};
|
|
26
40
|
}
|
|
27
|
-
|
|
28
|
-
return { manifest, implementation };
|
|
29
|
-
}
|
|
30
|
-
var artifactRefSchema = Type.Object({
|
|
31
|
-
path: Type.String()
|
|
32
|
-
});
|
|
33
|
-
var emptyWorkflowRefParamsSchema = Type.Object({});
|
|
41
|
+
var emptyWorkflowRefArgsSchema = Type.Object({});
|
|
34
42
|
var workflowReferenceInputSchema = Type.Union([
|
|
35
43
|
Type.String({ minLength: 1 }),
|
|
36
|
-
Type.Object({ workflow: Type.String({ minLength: 1 }),
|
|
44
|
+
Type.Object({ workflow: Type.String({ minLength: 1 }), forwardArgs: Type.Record(Type.String(), Type.Unknown()) })
|
|
37
45
|
]);
|
|
38
46
|
function workflowRefSchema(options) {
|
|
39
|
-
const contributionSchema = options?.
|
|
47
|
+
const contributionSchema = options?.args ?? emptyWorkflowRefArgsSchema;
|
|
40
48
|
return Type.With(Type.Decode(workflowReferenceInputSchema, (reference) => {
|
|
41
49
|
const workflowId = typeof reference === "string" ? reference : reference.workflow;
|
|
42
|
-
const
|
|
43
|
-
return (
|
|
44
|
-
Value.Assert(contributionSchema,
|
|
45
|
-
if (!isPlainObject(
|
|
46
|
-
Value.Assert(jsonValueSchema,
|
|
47
|
-
return createWorkflowTransition({ workflowId,
|
|
50
|
+
const forwardArgs = typeof reference === "string" ? {} : reference.forwardArgs;
|
|
51
|
+
return (args) => {
|
|
52
|
+
Value.Assert(contributionSchema, args);
|
|
53
|
+
if (!isPlainObject(args)) throw new Error("Workflow reference contributions must be objects");
|
|
54
|
+
Value.Assert(jsonValueSchema, args);
|
|
55
|
+
return createWorkflowTransition({ workflowId, args: { ...forwardArgs, ...args } });
|
|
48
56
|
};
|
|
49
|
-
}), { "x-norn-workflow-ref": {
|
|
50
|
-
}
|
|
51
|
-
function assertLocalDeclarationId(id, kind) {
|
|
52
|
-
if (id.length === 0) throw new Error(`Workflow ${kind} id must not be empty`);
|
|
53
|
-
if (id.includes(".")) throw new Error(`Workflow ${kind} id must not contain dots: ${id}`);
|
|
54
|
-
}
|
|
55
|
-
function resolveDeclarationId(pluginId, path, explicitId, kind, declaredIds) {
|
|
56
|
-
for (const segment of path) assertLocalDeclarationId(segment, kind);
|
|
57
|
-
const id = explicitId ?? [pluginId, ...path].join(".");
|
|
58
|
-
if (explicitId !== void 0) assertPluginQualifiedId(pluginId, explicitId, kind);
|
|
59
|
-
if (declaredIds.has(id)) throw new Error(`Duplicate Norn ${kind} id: ${id}`);
|
|
60
|
-
declaredIds.add(id);
|
|
61
|
-
return id;
|
|
62
|
-
}
|
|
63
|
-
function assertPluginQualifiedId(pluginId, id, kind) {
|
|
64
|
-
if (!id.startsWith(`${pluginId}.`)) throw new Error(`Explicit Norn ${kind} id must start with ${pluginId}.: ${id}`);
|
|
65
|
-
if (id.length === pluginId.length + 1) throw new Error(`Explicit Norn ${kind} id must not be empty after ${pluginId}.`);
|
|
57
|
+
}), { "x-norn-workflow-ref": { contributedArgsSchema: contributionSchema } });
|
|
66
58
|
}
|
|
67
|
-
function
|
|
68
|
-
|
|
69
|
-
const declaration = (params) => createWorkflowTransition({ workflowId: id, params });
|
|
70
|
-
return Object.assign(declaration, { ...workflow, kind: WORKFLOW_DECLARATION_KIND, id, isolation: workflow.isolation ?? { mode: "runWorkspace" } });
|
|
71
|
-
}
|
|
72
|
-
function qualifyStateTree(pluginId, node, path, declaredIds) {
|
|
73
|
-
if (node === void 0) return void 0;
|
|
74
|
-
if (isTypeboxSchema(node)) return { id: resolveDeclarationId(pluginId, path, void 0, "state", declaredIds), schema: node };
|
|
75
|
-
if (path.length > 0 && isWorkflowStateDefinitionInput(node)) {
|
|
76
|
-
return { ...node, id: resolveDeclarationId(pluginId, path, node.id, "state", declaredIds) };
|
|
77
|
-
}
|
|
78
|
-
if (!isPlainObject(node)) throw new Error(`Invalid state declaration: ${[pluginId, ...path].join(".")}`);
|
|
79
|
-
return Object.fromEntries(Object.entries(node).map(([key, child]) => [key, qualifyStateTree(pluginId, child, [...path, key], declaredIds)]));
|
|
59
|
+
function assertDeclarationId(id) {
|
|
60
|
+
if (typeof id !== "string" || id.trim().length === 0 || id.split(".").some((segment) => segment.length === 0)) throw new Error(`Invalid workflow or scope id: ${id}`);
|
|
80
61
|
}
|
|
81
62
|
function isWorkflowDeclaration(value) {
|
|
82
63
|
if (typeof value !== "function") return false;
|
|
83
64
|
const candidate = value;
|
|
84
|
-
return candidate.kind === WORKFLOW_DECLARATION_KIND && typeof candidate.id === "string" && candidate.id.length > 0 && (candidate.instructions === void 0 || typeof candidate.instructions === "string") && typeof candidate.isEntrypoint === "boolean" && Boolean(candidate.
|
|
85
|
-
}
|
|
86
|
-
function isTypeboxSchema(value) {
|
|
87
|
-
return Type.IsUnsafe(value) || Boolean(value && typeof value === "object" && "~kind" in value && typeof value["~kind"] === "string");
|
|
88
|
-
}
|
|
89
|
-
function isWorkflowStateDefinitionInput(value) {
|
|
90
|
-
if (!isPlainObject(value)) return false;
|
|
91
|
-
return (value.id === void 0 || typeof value.id === "string") && isTypeboxSchema(value.schema);
|
|
92
|
-
}
|
|
93
|
-
function isWorkflowPlugin(value) {
|
|
94
|
-
if (!value || typeof value !== "object") return false;
|
|
95
|
-
const candidate = value;
|
|
96
|
-
return isWorkflowPluginManifest(candidate.manifest) && Boolean(candidate.implementation);
|
|
97
|
-
}
|
|
98
|
-
function isWorkflowPluginManifest(value) {
|
|
99
|
-
if (!value || typeof value !== "object") return false;
|
|
100
|
-
const candidate = value;
|
|
101
|
-
if (typeof candidate.id !== "string" || candidate.id.length === 0) return false;
|
|
102
|
-
if (!candidate.workflows || typeof candidate.workflows !== "object") return false;
|
|
103
|
-
return Object.values(candidate.workflows).every(isWorkflowDeclaration);
|
|
65
|
+
return candidate.kind === WORKFLOW_DECLARATION_KIND && typeof candidate.id === "string" && candidate.id.length > 0 && (candidate.instructions === void 0 || typeof candidate.instructions === "string") && typeof candidate.isEntrypoint === "boolean" && Boolean(candidate.args) && typeof candidate.execute === "function";
|
|
104
66
|
}
|
|
105
67
|
function isWorkflowNext(value) {
|
|
106
68
|
if (!value || typeof value !== "object") return false;
|
|
@@ -118,14 +80,11 @@ function isWorkflowFail(value) {
|
|
|
118
80
|
return candidate.type === "fail" && typeof candidate.metadata?.summary === "string" && candidate.metadata.summary.length > 0;
|
|
119
81
|
}
|
|
120
82
|
export {
|
|
121
|
-
artifactRefSchema,
|
|
122
|
-
definePlugin,
|
|
123
|
-
definePluginManifest,
|
|
124
83
|
isWorkflowComplete,
|
|
125
84
|
isWorkflowDeclaration,
|
|
126
85
|
isWorkflowFail,
|
|
127
86
|
isWorkflowNext,
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
87
|
+
workflow,
|
|
88
|
+
workflowRefSchema,
|
|
89
|
+
workflowScope
|
|
131
90
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,2 @@
|
|
|
1
|
+
export type { CreateAgentSessionOptions, EventBus, PromptOptions, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
1
2
|
export * from "./api.ts";
|
|
2
|
-
export { NornFileCoordinator } from "./files.ts";
|
|
3
|
-
export * from "./resources.ts";
|
|
4
|
-
export * from "./agent-resource-adapter.ts";
|
|
5
|
-
export * from "./state-adapter.ts";
|
package/dist/index.js
CHANGED
package/dist/schema.js
CHANGED
|
@@ -23,9 +23,9 @@ function assertWorkflowReferenceAnnotations(schema) {
|
|
|
23
23
|
if (!isPlainObject(schema)) return;
|
|
24
24
|
if (Object.hasOwn(schema, "x-norn-workflow-ref")) {
|
|
25
25
|
const annotation = schema["x-norn-workflow-ref"];
|
|
26
|
-
if (!isPlainObject(annotation) || !Object.hasOwn(annotation, "
|
|
27
|
-
Value.Assert(Meta["https://json-schema.org/draft/2020-12/schema"], annotation.
|
|
28
|
-
assertWorkflowReferenceAnnotations(annotation.
|
|
26
|
+
if (!isPlainObject(annotation) || !Object.hasOwn(annotation, "contributedArgsSchema")) throw new Error("Invalid workflow reference annotation");
|
|
27
|
+
Value.Assert(Meta["https://json-schema.org/draft/2020-12/schema"], annotation.contributedArgsSchema);
|
|
28
|
+
assertWorkflowReferenceAnnotations(annotation.contributedArgsSchema);
|
|
29
29
|
}
|
|
30
30
|
for (const keyword of ["properties", "patternProperties", "$defs", "definitions", "dependentSchemas"]) {
|
|
31
31
|
const schemas = schema[keyword];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vimhead.dev/norn",
|
|
3
|
-
"version": "0.1.0-tip.
|
|
3
|
+
"version": "0.1.0-tip.35436871363.1",
|
|
4
4
|
"description": "TypeScript SDK for building agent-driven and code-driven Norn workflows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,10 +26,6 @@
|
|
|
26
26
|
"types": "./dist/index.d.ts",
|
|
27
27
|
"import": "./dist/index.js"
|
|
28
28
|
},
|
|
29
|
-
"./files": {
|
|
30
|
-
"types": "./dist/files.d.ts",
|
|
31
|
-
"import": "./dist/files.js"
|
|
32
|
-
},
|
|
33
29
|
"./schema": {
|
|
34
30
|
"types": "./dist/schema.d.ts",
|
|
35
31
|
"import": "./dist/schema.js"
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
export type NornAgentResourceBinding = {
|
|
3
|
-
readonly tools: readonly ToolDefinition[];
|
|
4
|
-
dispose(): Promise<void>;
|
|
5
|
-
};
|
|
6
|
-
export type NornAgentResourceAdapter = {
|
|
7
|
-
readonly name: string;
|
|
8
|
-
bind(context: {
|
|
9
|
-
readonly runId: string;
|
|
10
|
-
readonly label: string;
|
|
11
|
-
}): Promise<NornAgentResourceBinding>;
|
|
12
|
-
};
|
|
File without changes
|
package/dist/files.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export declare class NornFileCoordinator {
|
|
2
|
-
private readonly input;
|
|
3
|
-
private readonly lockRoot;
|
|
4
|
-
constructor(input: {
|
|
5
|
-
readonly lockRoot: string;
|
|
6
|
-
readonly waitTimeoutMs: number;
|
|
7
|
-
});
|
|
8
|
-
withExclusiveLock<T>(path: string, operation: (lockedPath: string) => Promise<T>): Promise<T>;
|
|
9
|
-
readText(path: string): Promise<string>;
|
|
10
|
-
writeText(path: string, content: string): Promise<void>;
|
|
11
|
-
private withCleanup;
|
|
12
|
-
private resolveTarget;
|
|
13
|
-
private acquire;
|
|
14
|
-
private reclaimDeadOwner;
|
|
15
|
-
private removeEmptyLock;
|
|
16
|
-
}
|
|
17
|
-
export declare function createRunFileCoordinator(runRoot: string): NornFileCoordinator;
|
package/dist/files.js
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
// ../core/src/atomic-files.ts
|
|
2
|
-
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { chmod, mkdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
4
|
-
import { dirname } from "node:path";
|
|
5
|
-
|
|
6
|
-
// ../core/src/errors.ts
|
|
7
|
-
function isNodeError(error) {
|
|
8
|
-
return error instanceof Error && "code" in error;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// ../core/src/atomic-files.ts
|
|
12
|
-
async function writeTextAtomically(path, content) {
|
|
13
|
-
await mkdir(dirname(path), { recursive: true });
|
|
14
|
-
let existingMode;
|
|
15
|
-
try {
|
|
16
|
-
existingMode = (await stat(path)).mode & 511;
|
|
17
|
-
} catch (error) {
|
|
18
|
-
if (!isNodeError(error) || error.code !== "ENOENT") throw error;
|
|
19
|
-
}
|
|
20
|
-
const tmpPath = `${path}.${randomUUID()}.tmp`;
|
|
21
|
-
try {
|
|
22
|
-
await writeFile(tmpPath, content, { encoding: "utf8", mode: existingMode });
|
|
23
|
-
if (existingMode !== void 0) await chmod(tmpPath, existingMode);
|
|
24
|
-
await rename(tmpPath, path);
|
|
25
|
-
} catch (error) {
|
|
26
|
-
await rm(tmpPath, { force: true }).catch(() => void 0);
|
|
27
|
-
throw error;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// src/files.ts
|
|
32
|
-
import { createHash, randomUUID as randomUUID2 } from "node:crypto";
|
|
33
|
-
import { lstat, mkdir as mkdir2, mkdtemp, readFile, readdir, realpath, rename as rename2, rm as rm2, rmdir, unlink, writeFile as writeFile2 } from "node:fs/promises";
|
|
34
|
-
import { hostname } from "node:os";
|
|
35
|
-
import { basename, dirname as dirname2, join, resolve } from "node:path";
|
|
36
|
-
import { setTimeout as delay } from "node:timers/promises";
|
|
37
|
-
import { Type } from "typebox";
|
|
38
|
-
import { Value } from "typebox/value";
|
|
39
|
-
var ownerSchema = Type.Object({
|
|
40
|
-
token: Type.String({ format: "uuid" }),
|
|
41
|
-
pid: Type.Integer({ exclusiveMinimum: 0 }),
|
|
42
|
-
host: Type.String(),
|
|
43
|
-
target: Type.String()
|
|
44
|
-
}, { additionalProperties: false });
|
|
45
|
-
var NornFileCoordinator = class {
|
|
46
|
-
constructor(input) {
|
|
47
|
-
this.input = input;
|
|
48
|
-
if (!Number.isSafeInteger(input.waitTimeoutMs) || input.waitTimeoutMs <= 0) throw new Error("Lock wait timeout must be a positive integer");
|
|
49
|
-
this.lockRoot = resolve(input.lockRoot);
|
|
50
|
-
}
|
|
51
|
-
input;
|
|
52
|
-
lockRoot;
|
|
53
|
-
async withExclusiveLock(path, operation) {
|
|
54
|
-
const target = await this.resolveTarget(path);
|
|
55
|
-
await mkdir2(this.lockRoot, { recursive: true, mode: 448 });
|
|
56
|
-
const key = createHash("sha256").update(target).digest("hex");
|
|
57
|
-
const lockPath = join(this.lockRoot, `${key}.lock`);
|
|
58
|
-
const stagingPath = await mkdtemp(join(this.lockRoot, `${key}.pending-`));
|
|
59
|
-
const token = randomUUID2();
|
|
60
|
-
const marker = `${token}.json`;
|
|
61
|
-
return this.withCleanup({
|
|
62
|
-
operation: async () => {
|
|
63
|
-
await writeFile2(join(stagingPath, marker), JSON.stringify({ token, pid: process.pid, host: hostname(), target }), { mode: 384 });
|
|
64
|
-
await this.acquire({ stagingPath, lockPath, target });
|
|
65
|
-
return this.withCleanup({
|
|
66
|
-
operation: () => operation(target),
|
|
67
|
-
cleanup: async () => {
|
|
68
|
-
await unlink(join(lockPath, marker));
|
|
69
|
-
await this.removeEmptyLock(lockPath);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
},
|
|
73
|
-
cleanup: () => rm2(stagingPath, { recursive: true, force: true })
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
async readText(path) {
|
|
77
|
-
return this.withExclusiveLock(path, (lockedPath) => readFile(lockedPath, "utf8"));
|
|
78
|
-
}
|
|
79
|
-
async writeText(path, content) {
|
|
80
|
-
await mkdir2(dirname2(path), { recursive: true });
|
|
81
|
-
await this.withExclusiveLock(path, (lockedPath) => writeTextAtomically(lockedPath, content));
|
|
82
|
-
}
|
|
83
|
-
async withCleanup(input) {
|
|
84
|
-
let result;
|
|
85
|
-
try {
|
|
86
|
-
result = await input.operation();
|
|
87
|
-
} catch (error) {
|
|
88
|
-
try {
|
|
89
|
-
await input.cleanup();
|
|
90
|
-
} catch (cleanupError) {
|
|
91
|
-
throw new AggregateError([error, cleanupError], "File operation and lock cleanup failed");
|
|
92
|
-
}
|
|
93
|
-
throw error;
|
|
94
|
-
}
|
|
95
|
-
await input.cleanup();
|
|
96
|
-
return result;
|
|
97
|
-
}
|
|
98
|
-
async resolveTarget(path) {
|
|
99
|
-
try {
|
|
100
|
-
return await realpath(path);
|
|
101
|
-
} catch (error) {
|
|
102
|
-
if (!isNodeError(error) || error.code !== "ENOENT") throw error;
|
|
103
|
-
const entry = await lstat(path).catch((error2) => {
|
|
104
|
-
if (isNodeError(error2) && error2.code === "ENOENT") return void 0;
|
|
105
|
-
throw error2;
|
|
106
|
-
});
|
|
107
|
-
if (entry?.isSymbolicLink()) throw new Error(`Cannot lock a dangling symbolic link: ${path}`);
|
|
108
|
-
return join(await realpath(dirname2(path)), basename(path));
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
async acquire(input) {
|
|
112
|
-
const deadline = Date.now() + this.input.waitTimeoutMs;
|
|
113
|
-
while (true) {
|
|
114
|
-
try {
|
|
115
|
-
await rename2(input.stagingPath, input.lockPath);
|
|
116
|
-
return;
|
|
117
|
-
} catch (error) {
|
|
118
|
-
if (!isNodeError(error) || !["ENOTEMPTY", "EEXIST"].includes(error.code ?? "")) throw error;
|
|
119
|
-
}
|
|
120
|
-
await this.reclaimDeadOwner(input);
|
|
121
|
-
if (Date.now() >= deadline) throw new Error(`Timed out waiting for file lock: ${input.target}`);
|
|
122
|
-
await delay(10);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
async reclaimDeadOwner(input) {
|
|
126
|
-
try {
|
|
127
|
-
const entries = await readdir(input.lockPath);
|
|
128
|
-
if (entries.length === 0) return;
|
|
129
|
-
if (entries.length !== 1) throw new Error(`Invalid file lock ownership: ${input.lockPath}`);
|
|
130
|
-
const marker = entries[0];
|
|
131
|
-
const owner = Value.Parse(ownerSchema, JSON.parse(await readFile(join(input.lockPath, marker), "utf8")));
|
|
132
|
-
if (marker !== `${owner.token}.json` || owner.target !== input.target || owner.host !== hostname()) {
|
|
133
|
-
throw new Error(`Incompatible file lock ownership: ${input.lockPath}`);
|
|
134
|
-
}
|
|
135
|
-
try {
|
|
136
|
-
process.kill(owner.pid, 0);
|
|
137
|
-
return;
|
|
138
|
-
} catch (error) {
|
|
139
|
-
if (isNodeError(error) && error.code === "EPERM") return;
|
|
140
|
-
if (!isNodeError(error) || error.code !== "ESRCH") throw error;
|
|
141
|
-
}
|
|
142
|
-
await unlink(join(input.lockPath, marker));
|
|
143
|
-
await this.removeEmptyLock(input.lockPath);
|
|
144
|
-
} catch (error) {
|
|
145
|
-
if (!isNodeError(error) || error.code !== "ENOENT") throw error;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
async removeEmptyLock(path) {
|
|
149
|
-
try {
|
|
150
|
-
await rmdir(path);
|
|
151
|
-
} catch (error) {
|
|
152
|
-
if (!isNodeError(error) || !["ENOENT", "ENOTEMPTY", "EEXIST"].includes(error.code ?? "")) throw error;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
function createRunFileCoordinator(runRoot) {
|
|
157
|
-
return new NornFileCoordinator({ lockRoot: join(runRoot, "locks"), waitTimeoutMs: 3e4 });
|
|
158
|
-
}
|
|
159
|
-
export {
|
|
160
|
-
NornFileCoordinator,
|
|
161
|
-
createRunFileCoordinator
|
|
162
|
-
};
|
package/dist/resources.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { NornFileCoordinator } from "./files.ts";
|
|
2
|
-
import type { NornJsonValue } from "./schema.ts";
|
|
3
|
-
export type NornResourceContext = {
|
|
4
|
-
readonly mode: "create" | "open";
|
|
5
|
-
readonly directory: string;
|
|
6
|
-
readonly files: NornFileCoordinator;
|
|
7
|
-
};
|
|
8
|
-
export type NornResourceDefinition<T> = {
|
|
9
|
-
readonly name: string;
|
|
10
|
-
readonly kind: string;
|
|
11
|
-
readonly configuration: NornJsonValue;
|
|
12
|
-
initialize(context: NornResourceContext): Promise<T>;
|
|
13
|
-
};
|
|
14
|
-
export type NornResources = {
|
|
15
|
-
readonly files: NornFileCoordinator;
|
|
16
|
-
ensure<T>(definition: NornResourceDefinition<T>): Promise<T>;
|
|
17
|
-
};
|
package/dist/resources.js
DELETED
|
File without changes
|
package/dist/state-adapter.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { NornAgentResourceAdapter } from "./agent-resource-adapter.ts";
|
|
2
|
-
import type { NornWorkflowState, NornWorkflowStateDefinition } from "./api.ts";
|
|
3
|
-
export type NornStateFieldAccess = {
|
|
4
|
-
readonly field: NornWorkflowStateDefinition;
|
|
5
|
-
readonly access: "read" | "write" | "read-write";
|
|
6
|
-
};
|
|
7
|
-
export declare function StateAdapter(input: {
|
|
8
|
-
readonly state: NornWorkflowState;
|
|
9
|
-
readonly fields: readonly NornStateFieldAccess[];
|
|
10
|
-
}): NornAgentResourceAdapter;
|
package/dist/state-adapter.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
// src/state-adapter.ts
|
|
2
|
-
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { createHash } from "node:crypto";
|
|
4
|
-
import { Type } from "typebox";
|
|
5
|
-
import { inspectSchema } from "./schema.js";
|
|
6
|
-
var pageParameters = {
|
|
7
|
-
offset: Type.Integer({ minimum: 0, description: "Zero-based UTF-16 offset into the serialized JSON. Start at 0." }),
|
|
8
|
-
limit: Type.Integer({ minimum: 1, maximum: 1e4 })
|
|
9
|
-
};
|
|
10
|
-
function StateAdapter(input) {
|
|
11
|
-
const fields = new Map(input.fields.map((grant) => [grant.field.id, grant]));
|
|
12
|
-
if (fields.size !== input.fields.length || fields.size === 0) throw new Error("State attachment requires unique, explicitly selected fields");
|
|
13
|
-
const selectField = (key, access) => {
|
|
14
|
-
const grant = fields.get(key);
|
|
15
|
-
if (!grant || grant.access !== access && grant.access !== "read-write") throw new Error(`State ${access} is not attached: ${key}`);
|
|
16
|
-
return grant.field;
|
|
17
|
-
};
|
|
18
|
-
return {
|
|
19
|
-
name: "norn.state",
|
|
20
|
-
async bind() {
|
|
21
|
-
return {
|
|
22
|
-
tools: [
|
|
23
|
-
defineTool({
|
|
24
|
-
name: "norn_state_list",
|
|
25
|
-
label: "Attached workflow state",
|
|
26
|
-
description: "List only attached workflow-state field IDs, permissions and value schemas. JSON is paginated; use nextOffset until null.",
|
|
27
|
-
parameters: Type.Object(pageParameters),
|
|
28
|
-
async execute(_id, params) {
|
|
29
|
-
return serializePage({ value: [...fields.values()].map(({ field, access }) => ({ id: field.id, access, schema: inspectSchema(field.schema) })), ...params });
|
|
30
|
-
}
|
|
31
|
-
}),
|
|
32
|
-
defineTool({
|
|
33
|
-
name: "norn_state_get",
|
|
34
|
-
label: "Read workflow state",
|
|
35
|
-
description: "Read a selected workflow-state field. Unset fields return isSet:false. JSON is paginated; concurrent writes can change later pages, so compare revision before combining pages.",
|
|
36
|
-
parameters: Type.Object({ key: Type.String(), ...pageParameters }),
|
|
37
|
-
async execute(_id, params) {
|
|
38
|
-
const value = await input.state.getOptional(selectField(params.key, "read"));
|
|
39
|
-
return serializePage({ value: value === void 0 ? { isSet: false } : { isSet: true, value }, ...params });
|
|
40
|
-
}
|
|
41
|
-
}),
|
|
42
|
-
defineTool({
|
|
43
|
-
name: "norn_state_set",
|
|
44
|
-
label: "Write workflow state",
|
|
45
|
-
description: "Set an explicitly writable workflow-state field. Validate the value against its schema from norn_state_list. A get followed by set is not a transaction.",
|
|
46
|
-
parameters: Type.Object({ key: Type.String(), value: Type.Unknown() }),
|
|
47
|
-
async execute(_id, params, signal) {
|
|
48
|
-
signal?.throwIfAborted();
|
|
49
|
-
const field = selectField(params.key, "write");
|
|
50
|
-
await input.state.set(field, params.value);
|
|
51
|
-
return { content: [{ type: "text", text: "Workflow state saved." }], details: {} };
|
|
52
|
-
}
|
|
53
|
-
})
|
|
54
|
-
],
|
|
55
|
-
async dispose() {
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
function serializePage(input) {
|
|
62
|
-
const serialized = JSON.stringify(input.value);
|
|
63
|
-
if (!Number.isInteger(input.offset) || input.offset < 0 || !Number.isInteger(input.limit) || input.limit < 1 || input.limit > 1e4) throw new Error("Invalid state output page");
|
|
64
|
-
const end = Math.min(serialized.length, input.offset + input.limit);
|
|
65
|
-
const details = { text: serialized.slice(input.offset, end), nextOffset: end < serialized.length ? end : null, revision: createHash("sha256").update(serialized).digest("hex") };
|
|
66
|
-
return { content: [{ type: "text", text: JSON.stringify(details) }], details };
|
|
67
|
-
}
|
|
68
|
-
export {
|
|
69
|
-
StateAdapter
|
|
70
|
-
};
|