@mawaru/sdk 0.13.0 → 0.14.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/_schemas/ai-manifest.test.ts +54 -59
- package/_schemas/ai-manifest.ts +38 -23
- package/_schemas/extract-component.test.ts +51 -2
- package/_schemas/extract-component.ts +4 -2
- package/_schemas/graph.test.ts +87 -66
- package/_schemas/graph.ts +14 -29
- package/_schemas/node.ts +4 -0
- package/_schemas/port-spec.test.ts +120 -129
- package/_schemas/port-spec.ts +70 -106
- package/dist/_schemas/ai-manifest.d.ts +12 -1
- package/dist/_schemas/ai-manifest.js +35 -20
- package/dist/_schemas/extract-component.js +4 -2
- package/dist/_schemas/graph.d.ts +5 -0
- package/dist/_schemas/graph.js +15 -26
- package/dist/_schemas/node.d.ts +1 -0
- package/dist/_schemas/node.js +4 -0
- package/dist/_schemas/port-spec.d.ts +2 -5
- package/dist/_schemas/port-spec.js +57 -89
- package/dist/typegen.js +6 -6
- package/docs/development.md +5 -1
- package/package.json +2 -3
- package/skills/create-loop/SKILL.md +1 -1
- package/templates/CLAUDE.md +1 -1
- package/templates/echo/main.ts +1 -1
- package/templates/mawaru-runner.yml +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
// ポートの kind 別型システム(docs/tasks/wip/ポートのkind別仕様.md)。
|
|
3
|
-
// 型の決まり方は
|
|
4
|
-
//
|
|
3
|
+
// 型の決まり方は2分類:宣言(program / ai の in・out。human はビューの宣言を転写)/
|
|
4
|
+
// 導出(それ以外。接続元から伝播し、結果は ports.schema にキャッシュする)。
|
|
5
|
+
// mawaru がコードで型を固定するポートは無い
|
|
6
|
+
// (docs/tasks/wip/ポート型固定の撤去とセッション再開のポート属性化.md)
|
|
5
7
|
// 「まだ型が決まっていない」=未宣言を {} で表す(未接続・上流が未宣言・fan-in の型割れ)。
|
|
6
|
-
// 「何でも受ける」型は存在しない:
|
|
7
|
-
//
|
|
8
|
+
// 「何でも受ける」型は存在しない:program / ai の in は接続元の具体型を鏡映する
|
|
9
|
+
// (docs/tasks/wip/ポートスキーマのany撲滅.md)
|
|
8
10
|
export const UNDECLARED_SCHEMA = {};
|
|
9
11
|
export const isUndeclaredSchema = (schema) => Object.keys(schema).length === 0 || schema.type === undefined;
|
|
10
12
|
// ---- file 型(docs/tasks/done/AIノードのファイル入力(file型と添付).md) ----
|
|
@@ -28,13 +30,12 @@ export const fileSchema = () => ({
|
|
|
28
30
|
required: ["path", "name"],
|
|
29
31
|
});
|
|
30
32
|
export const isFileSchema = (schema) => schema[FILE_SCHEMA_MARKER] === FILE_SCHEMA_TYPE;
|
|
31
|
-
// ----
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
export const AI_INSTRUCTION_PORT_KEY = "instruction";
|
|
33
|
+
// ---- 差し戻しの指示のデフォルト型 ----
|
|
34
|
+
// 雛形(エディタの既定ポート・ais/create)と承認ビューの既定宣言が参照する
|
|
35
|
+
// **デフォルトであって強制ではない**:ユーザーはポートの宣言を自由に変えられ、
|
|
36
|
+
// 接続の成立は通常の型検証だけが決める。配管が効く根拠は files が file 型として
|
|
37
|
+
// 宣言されていることであって、キー名ではない(runner がマーカーで位置を見つけて
|
|
38
|
+
// materialize する)。スクリーンショットを添えて差し戻すケースがあるので文章だけにしない
|
|
38
39
|
export const instructionSchema = () => ({
|
|
39
40
|
type: "object",
|
|
40
41
|
properties: {
|
|
@@ -116,36 +117,6 @@ export const dataFormatOf = (schema) => {
|
|
|
116
117
|
return null;
|
|
117
118
|
return { format: "list", columns };
|
|
118
119
|
};
|
|
119
|
-
// 封筒型 { data: T, description: string }。T は任意の JSON Schema、description は
|
|
120
|
-
// AI によるデータの説明(AI が必ず生成する)。AI の全出口に強制する
|
|
121
|
-
export const envelopeSchemaOf = (dataSchema) => ({
|
|
122
|
-
type: "object",
|
|
123
|
-
properties: {
|
|
124
|
-
data: dataSchema,
|
|
125
|
-
description: { type: "string" },
|
|
126
|
-
},
|
|
127
|
-
required: ["data", "description"],
|
|
128
|
-
});
|
|
129
|
-
// 封筒型なら data のサブスキーマを返す(data キー必須の object。それ以外は null)
|
|
130
|
-
export const envelopeDataSchemaOf = (schema) => {
|
|
131
|
-
if (schema.type !== "object")
|
|
132
|
-
return null;
|
|
133
|
-
const properties = (schema.properties ?? {});
|
|
134
|
-
const required = (schema.required ?? []);
|
|
135
|
-
const data = properties.data;
|
|
136
|
-
if (!data || !required.includes("data"))
|
|
137
|
-
return null;
|
|
138
|
-
return data;
|
|
139
|
-
};
|
|
140
|
-
// AI の出口として妥当な封筒型か:data と description: string の両方が必須
|
|
141
|
-
export const isAiEnvelopeSchema = (schema) => {
|
|
142
|
-
if (envelopeDataSchemaOf(schema) === null)
|
|
143
|
-
return false;
|
|
144
|
-
const properties = (schema.properties ?? {});
|
|
145
|
-
const required = (schema.required ?? []);
|
|
146
|
-
return (properties.description?.type === "string" &&
|
|
147
|
-
required.includes("description"));
|
|
148
|
-
};
|
|
149
120
|
// ---- kind 別ポート構成 ----
|
|
150
121
|
// human / wait は構成(数・key・io)が固定。program / ai は in 1つ+out 1つ以上
|
|
151
122
|
// (key 自由。複数 out=分岐。AIノードの複数出口(分岐).md)
|
|
@@ -160,19 +131,16 @@ const FIXED_PORT_KEYS = {
|
|
|
160
131
|
component: { in: ["in"], out: ["main"] },
|
|
161
132
|
};
|
|
162
133
|
const FREE_OUT_KINDS = new Set(["program", "ai"]);
|
|
163
|
-
// out
|
|
164
|
-
//
|
|
165
|
-
//
|
|
134
|
+
// out は自由(複数出口=分岐)。program は in の構成も固定(1口)だが、
|
|
135
|
+
// ai は in も自由(追加・削除・key 自由。セッション再開は key ではなくポートの
|
|
136
|
+
// resume 属性で決まる。ポート型固定の撤去とセッション再開のポート属性化.md)
|
|
166
137
|
const FIXED_IN_KEYS = {
|
|
167
138
|
program: ["in"],
|
|
168
|
-
ai: ["in", AI_INSTRUCTION_PORT_KEY],
|
|
169
139
|
};
|
|
170
140
|
export const fixedInKeysOf = (kind) => FIXED_IN_KEYS[kind] ?? null;
|
|
171
141
|
// config.json(manifest)の inputs 検査(適合なら null)。同じルールを二重に持たないため
|
|
172
|
-
// manifest
|
|
173
|
-
//
|
|
174
|
-
// constSchemaOf が焼き込む固定型なのでエディタ側で補え、in を2口に分ける前に同期された
|
|
175
|
-
// config.json もそのまま読める
|
|
142
|
+
// manifest 側はここを参照する。in の構成が固定の kind(program)だけが使う
|
|
143
|
+
// (ai の inputs は key 自由なので ai-manifest 側で件数と key の規則だけ見る)
|
|
176
144
|
export const manifestInKeysViolation = (kind, keys) => {
|
|
177
145
|
const allowed = fixedInKeysOf(kind) ?? ["in"];
|
|
178
146
|
if (!keys.includes("in"))
|
|
@@ -225,13 +193,12 @@ export const kindPortsViolation = (kind, ports) => {
|
|
|
225
193
|
};
|
|
226
194
|
// ---- 型伝播(導出ポートの解決) ----
|
|
227
195
|
// 接続の両端は同じスキーマになる(値入力フォームのSchemaForm統一.md)。
|
|
228
|
-
// wait
|
|
229
|
-
//
|
|
230
|
-
// human の reject は未宣言のまま(段階2 で instruction エンベロープを宣言する)。
|
|
196
|
+
// wait は素通し(パラメトリック)ノード:in は接続元の out、out は in と同型。
|
|
197
|
+
// human はビューの宣言を転写("T" は素通しと同じ挙動。reject もビューの宣言)。
|
|
231
198
|
// 宣言 out(program / ai)は触らない。
|
|
232
|
-
// program / ai の in
|
|
233
|
-
//
|
|
234
|
-
// へも書き戻される。docs/tasks/wip/ポート編集のrepo書き戻し同期.md)
|
|
199
|
+
// program / ai の in は**全ての口が**接続鏡映(sticky):接続元 out が具体型のときだけ
|
|
200
|
+
// 上書きし、未接続・型割れ・未宣言は現スキーマ(宣言)を維持する(鏡映結果は repo の
|
|
201
|
+
// config.json へも書き戻される。docs/tasks/wip/ポート編集のrepo書き戻し同期.md)
|
|
235
202
|
// 複数の流入(fan-in)から型を1つに決める:未宣言を除いて具体型が1種類ならその型、
|
|
236
203
|
// 0種類なら未宣言。2種類以上は型が割れているので未宣言に落とす
|
|
237
204
|
// (保存時に graph.ts が 422 で弾く。伝播は止めずに固定点へ収束させる)
|
|
@@ -258,34 +225,22 @@ const DERIVED_KINDS = new Set(["wait", "human", "end", "start"]);
|
|
|
258
225
|
// in を接続鏡映(sticky)するノード:接続元 out が具体型のときだけ上書き
|
|
259
226
|
const MIRROR_KINDS = new Set(["program", "ai"]);
|
|
260
227
|
const isDerivedPort = (kind, port) => DERIVED_KINDS.has(kind) &&
|
|
261
|
-
(port.io === "in" ||
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
// 宣言型で固定されるポート(導出も鏡映もしない)。差し戻しの指示エンベロープを
|
|
266
|
-
// human の出口と AI の入口の両方に焼き込むことで、両端が同じ具体型で一致する
|
|
267
|
-
const constSchemaOf = (kind, port) => {
|
|
268
|
-
if (kind === "human" && port.io === "out" && port.key === "reject") {
|
|
269
|
-
return instructionSchema();
|
|
270
|
-
}
|
|
271
|
-
if (kind === "ai" &&
|
|
272
|
-
port.io === "in" &&
|
|
273
|
-
port.key === AI_INSTRUCTION_PORT_KEY) {
|
|
274
|
-
return instructionSchema();
|
|
275
|
-
}
|
|
276
|
-
return null;
|
|
277
|
-
};
|
|
228
|
+
(port.io === "in" ||
|
|
229
|
+
port.key === "main" ||
|
|
230
|
+
port.key === "approve" ||
|
|
231
|
+
port.key === "reject");
|
|
278
232
|
// port_id → 解決済みスキーマ。導出は接続を遡って固定点まで反復し、
|
|
279
|
-
// 未接続・純パラメトリック閉路・fan-in
|
|
233
|
+
// 未接続・純パラメトリック閉路・fan-in の型割れは未宣言に落とす。
|
|
234
|
+
// ai の in は nodes/ai/<dir>/config.json の inputs[key].schema 由来の宣言型が初期値で、
|
|
235
|
+
// 接続時は program と同じ鏡映で接続元と同値に同期される(どの in も対等。
|
|
236
|
+
// instruction への型の焼き込みは廃止)
|
|
280
237
|
export const resolvePortSchemas = (nodes, connections, options) => {
|
|
281
238
|
const resolved = new Map();
|
|
282
239
|
const kindByPortId = new Map();
|
|
283
240
|
for (const node of nodes) {
|
|
284
241
|
for (const port of node.ports) {
|
|
285
242
|
kindByPortId.set(port.id, node.kind);
|
|
286
|
-
|
|
287
|
-
resolved.set(port.id, constSchema ??
|
|
288
|
-
(isDerivedPort(node.kind, port) ? UNDECLARED_SCHEMA : port.schema));
|
|
243
|
+
resolved.set(port.id, isDerivedPort(node.kind, port) ? UNDECLARED_SCHEMA : port.schema);
|
|
289
244
|
}
|
|
290
245
|
}
|
|
291
246
|
// in ポートへの流入(差し戻しの fan-in があり得る。1本のときだけ型を採用)
|
|
@@ -337,20 +292,28 @@ export const resolvePortSchemas = (nodes, connections, options) => {
|
|
|
337
292
|
const isMirror = MIRROR_KINDS.has(node.kind);
|
|
338
293
|
if (!DERIVED_KINDS.has(node.kind) && !isMirror)
|
|
339
294
|
continue;
|
|
340
|
-
// ai
|
|
341
|
-
|
|
295
|
+
// 鏡映(program / ai):全ての in が対象。具体型が流れてきたときだけ上書きする
|
|
296
|
+
// (宣言を未宣言で潰さない)。ai の in はどの口も対等(instruction の特別扱いは無い)
|
|
297
|
+
if (isMirror) {
|
|
298
|
+
for (const port of node.ports) {
|
|
299
|
+
if (port.io !== "in")
|
|
300
|
+
continue;
|
|
301
|
+
const sources = inbound.get(port.id) ?? [];
|
|
302
|
+
const inSchema = mergeInboundSchemas(sources.map((id) => resolved.get(id) ?? UNDECLARED_SCHEMA));
|
|
303
|
+
if (!isUndeclaredSchema(inSchema))
|
|
304
|
+
assign(port.id, inSchema);
|
|
305
|
+
}
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
// 導出(wait / human):in は上流から
|
|
309
|
+
const inPort = node.ports.find((p) => p.io === "in");
|
|
342
310
|
if (!inPort)
|
|
343
311
|
continue;
|
|
344
312
|
const sources = inbound.get(inPort.id) ?? [];
|
|
345
313
|
const inSchema = mergeInboundSchemas(sources.map((id) => resolved.get(id) ?? UNDECLARED_SCHEMA));
|
|
346
|
-
// 鏡映(program / ai)は具体型が流れてきたときだけ上書き(宣言を未宣言で潰さない)
|
|
347
|
-
if (isMirror) {
|
|
348
|
-
if (!isUndeclaredSchema(inSchema))
|
|
349
|
-
assign(inPort.id, inSchema);
|
|
350
|
-
continue;
|
|
351
|
-
}
|
|
352
314
|
// 承認ビューの宣言を human のポートへそのまま転写する(上流ノードの kind は見ない)。
|
|
353
|
-
// "T" は「上流の out をコピー」(in)/「in と同型」(approve)。
|
|
315
|
+
// "T" は「上流の out をコピー」(in)/「in と同型」(approve)。reject は具体型の宣言
|
|
316
|
+
// のみで、宣言が無ければ未宣言のまま(mawaru が型を焼き込むことはしない)。
|
|
354
317
|
// 上流の out は書き換えない:逆伝播すると fan-out 先のビューが食い違ったとき
|
|
355
318
|
// 型割れが config.json へ write-through され、元の出力宣言を黙って壊す。
|
|
356
319
|
// ズレは接続の型エラーで見せてユーザーの [修正する] で直す
|
|
@@ -360,11 +323,16 @@ export const resolvePortSchemas = (nodes, connections, options) => {
|
|
|
360
323
|
: inSchema;
|
|
361
324
|
assign(inPort.id, effectiveIn);
|
|
362
325
|
for (const port of node.ports) {
|
|
363
|
-
if (port.io
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
326
|
+
if (port.io !== "out" || !isDerivedPort(node.kind, port))
|
|
327
|
+
continue;
|
|
328
|
+
if (node.kind === "human" && port.key === "reject") {
|
|
329
|
+
if (declared)
|
|
330
|
+
assign(port.id, declared.reject);
|
|
331
|
+
continue;
|
|
367
332
|
}
|
|
333
|
+
assign(port.id, declared && declared.approve !== COPY_UPSTREAM_SCHEMA
|
|
334
|
+
? declared.approve
|
|
335
|
+
: effectiveIn);
|
|
368
336
|
}
|
|
369
337
|
}
|
|
370
338
|
if (!changed)
|
package/dist/typegen.js
CHANGED
|
@@ -108,11 +108,11 @@ export const tsTypeFromJsonSchema = (schema, indent = 0) => {
|
|
|
108
108
|
}
|
|
109
109
|
return "unknown";
|
|
110
110
|
};
|
|
111
|
-
// Output(run の戻り値)=「ちょうど1
|
|
111
|
+
// Output(run の戻り値)=「ちょうど1つの出口ポートのキーで返す { <出口キー>: 値 }」の union。
|
|
112
112
|
// 他ポートのキーを ?: never で締めるのは excess property check の穴
|
|
113
|
-
// (union
|
|
114
|
-
// エンジン(
|
|
115
|
-
const
|
|
113
|
+
// (union の他メンバーにあるキーは許される)を塞ぐため。複数キーの戻り値は
|
|
114
|
+
// エンジン(resolvePortKeyedOutput)が実行時に拒否する契約で、それを tsc に前倒しする
|
|
115
|
+
const outputUnionType = (outputKeys) => {
|
|
116
116
|
const member = (key) => {
|
|
117
117
|
const props = [
|
|
118
118
|
`${propertyKey(key)}: Outputs[${JSON.stringify(key)}]`,
|
|
@@ -142,8 +142,8 @@ export const renderTypesFile = (manifest, rawConfig) => {
|
|
|
142
142
|
"",
|
|
143
143
|
`export type Outputs = {\n${outputEntries.join("\n")}\n}`,
|
|
144
144
|
"",
|
|
145
|
-
"// run の戻り値:ちょうど1
|
|
146
|
-
|
|
145
|
+
"// run の戻り値:ちょうど1つの出口ポートのキーで返す { <出口キー>: 値 }",
|
|
146
|
+
outputUnionType(Object.keys(manifest.outputs)),
|
|
147
147
|
"",
|
|
148
148
|
...(refEntries.length > 0
|
|
149
149
|
? [
|
package/docs/development.md
CHANGED
|
@@ -27,7 +27,7 @@ skills/<dir>/ # AI ノードが参照する skill(SKILL.md)
|
|
|
27
27
|
- `config.json` — 入出力スキーマ等の宣言。正は `_schemas/program-manifest.ts`
|
|
28
28
|
(`inputs` は `"in"` の1件だけ・`outputs` は1件以上・`env` は必要な secret 名の宣言)
|
|
29
29
|
- `main.ts` — `run(input, ctx)` を export する。戻り値:
|
|
30
|
-
- `{ <出口ポートのkey>: <データ> }`
|
|
30
|
+
- `{ <出口ポートのkey>: <データ> }` の**単一キーのオブジェクト**(どの出口から出るかを戻り値自身が運ぶ)
|
|
31
31
|
- `"pending"` — 完了保留(外部イベントで後から end が届く)
|
|
32
32
|
- `ctx` — ループ変数の袋の読み書き:
|
|
33
33
|
- `ctx.refs` — 届いた袋(loop に定義された変数の、この step 開始時点の値)
|
|
@@ -46,6 +46,10 @@ skills/<dir>/ # AI ノードが参照する skill(SKILL.md)
|
|
|
46
46
|
|
|
47
47
|
- `config.json` — 正は `_schemas/ai-manifest.ts`。`prompt`(必須)・`model`・
|
|
48
48
|
`skills`(トップレベル `skills/` 配下のディレクトリ名)・`env`・`inputs` / `outputs`
|
|
49
|
+
- `inputs` は1件以上・key 自由。値は `{ "schema": <JSON Schema>, "resume": true }` の形
|
|
50
|
+
(`resume` は省略可。true の口から入力が届いた実行は、同じノードの直近セッションを
|
|
51
|
+
復元して再開する——差し戻し→修正の続き実行に使う)。旧形式(値が素の JSON Schema)も
|
|
52
|
+
読める
|
|
49
53
|
- 実行時はラッパーが Claude Code(headless)を起動し、`outputs` のスキーマに適合する
|
|
50
54
|
output を検証して報告する。出口が複数あれば AI が1つ選ぶ(複数出口=分岐)
|
|
51
55
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mawaru/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.1",
|
|
4
4
|
"description": "mawaru 実行 repo の開発 SDK。契約スキーマ(config.json 規約・ループ graph API)の正 + typegen / validate CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -36,7 +36,6 @@
|
|
|
36
36
|
"build": "tsc -p tsconfig.build.json",
|
|
37
37
|
"typecheck": "tsc --noEmit",
|
|
38
38
|
"test": "vitest run",
|
|
39
|
-
"test:watch": "vitest"
|
|
40
|
-
"release": "sh -c 'pnpm typecheck && pnpm test && npm version --no-git-tag-version ${1:-minor} && pnpm publish --access public --no-git-checks' release"
|
|
39
|
+
"test:watch": "vitest"
|
|
41
40
|
}
|
|
42
41
|
}
|
|
@@ -49,7 +49,7 @@ secret)でも、**足りない値を推測・生成・空文字で代用しな
|
|
|
49
49
|
ノード・接続・refs(ループ変数)・hooks の形と、superRefine 内の全整合性ルール(日本語コメント付き)
|
|
50
50
|
- `node_modules/@mawaru/sdk/_schemas/node.ts` — ポートの形(`graphPortSchema`)と key の規則
|
|
51
51
|
- `node_modules/@mawaru/sdk/_schemas/port-spec.ts` — **kind 別の必須ポート構成**(`kindPortsViolation`)と
|
|
52
|
-
接続の型規則(human の in
|
|
52
|
+
接続の型規則(human の in は承認ビューの宣言型、program の in はスキーマ包含 等)
|
|
53
53
|
- `node_modules/@mawaru/sdk/_schemas/program-manifest.ts` / `ai-manifest.ts` — repo 側 config.json の規約
|
|
54
54
|
- 同ディレクトリの `*.test.ts` — 通る graph / 弾かれる graph の実例集として読める
|
|
55
55
|
|
package/templates/CLAUDE.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
## 構成
|
|
8
8
|
|
|
9
9
|
- `nodes/program/<dir>/` — Program ノード(`config.json` + `main.ts`)。`main.ts` は
|
|
10
|
-
`run(input, ctx)` を export し、戻り値は `{ <出口ポートのkey>: <データ> }`
|
|
10
|
+
`run(input, ctx)` を export し、戻り値は `{ <出口ポートのkey>: <データ> }` の単一キーのオブジェクト
|
|
11
11
|
- `nodes/ai/<dir>/` — AI ノード定義(`config.json`。prompt / model / skills / 入出力スキーマ / env)
|
|
12
12
|
- `.github/workflows/mawaru-runner.yml` — mawaru 所有の実行シム。**編集しない**
|
|
13
13
|
(`npx @mawaru/sdk init` が再生成する)
|
package/templates/echo/main.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// サンプル handler:入力をそのまま main 出口へ返す。
|
|
2
|
-
// 戻り値は { <出口ポートのkey>: <データ> }
|
|
2
|
+
// 戻り値は { <出口ポートのkey>: <データ> } の単一キーのオブジェクト(出口の選別を戻り値自身が運ぶ)。
|
|
3
3
|
// 入出力の形は同じディレクトリの config.json が正(`npx mawaru typegen` で型も生成できる)
|
|
4
4
|
export const run = async (input: { message: string }) => ({ main: input })
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# 自分で分岐する。ラッパーは config.json(nodes/program/<dir>/・nodes/ai/<dir>/)の env 宣言で
|
|
8
8
|
# secrets を絞り、結果を検証して end_url へ「自分で」報告する。
|
|
9
9
|
# Program handler は main.ts が run(input, ctx) を export し、戻り値={ <出口ポートのkey>: <データ> }
|
|
10
|
-
#
|
|
10
|
+
# の単一キーのオブジェクト(出口の選別を戻り値自身が運ぶ)か "pending"(完了保留)で結末を表す。
|
|
11
11
|
# ファイル I/O(input.json / output.json / pending.json)はラッパーが肩代わりする
|
|
12
12
|
# (run を export しない旧スクリプト規約も後方互換で動く)。
|
|
13
13
|
# client_payload.step_id は冪等キー:is_irreversible な処理は step_id で二重実行を弾くこと。
|