@mawaru/sdk 0.40.0 → 0.41.0
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/graph.ts +0 -5
- package/_schemas/human-manifest.test.ts +39 -101
- package/_schemas/human-manifest.ts +25 -152
- package/_schemas/port-spec.test.ts +33 -92
- package/_schemas/port-spec.ts +35 -44
- package/dist/_cli/login.js +15 -1
- package/dist/_cli/previewApi.d.ts +1 -1
- package/dist/_cli/previewCommand.d.ts +0 -8
- package/dist/_cli/previewCommand.js +37 -95
- package/dist/_cli/previewFiles.d.ts +1 -2
- package/dist/_cli/previewFiles.js +18 -42
- package/dist/_schemas/graph.d.ts +0 -15
- package/dist/_schemas/graph.js +0 -5
- package/dist/_schemas/human-manifest.d.ts +6 -24
- package/dist/_schemas/human-manifest.js +24 -116
- package/dist/_schemas/port-spec.d.ts +1 -4
- package/dist/_schemas/port-spec.js +25 -34
- package/dist/cli.js +1 -2
- package/dist/typegen.d.ts +2 -2
- package/dist/typegen.js +21 -21
- package/dist/validate.js +9 -7
- package/dist/views/_humanProps.d.ts +10 -0
- package/dist/views/_humanProps.js +11 -0
- package/dist/views/{index.d.ts → components.d.ts} +0 -1
- package/dist/views/components.js +12 -0
- package/dist/views/host.d.ts +8 -3
- package/dist/views/host.js +5 -5
- package/dist/views/human.d.ts +49 -68
- package/dist/views/human.js +5 -6
- package/dist/views/protocol.d.ts +13 -3
- package/dist/views/protocol.js +2 -2
- package/dist/views/runtime.d.ts +2 -3
- package/dist/views/runtime.js +24 -24
- package/dist/views/sample.d.ts +22 -0
- package/dist/views/sample.js +38 -0
- package/docs/development.md +30 -31
- package/package.json +5 -1
- package/skills/create-loop/SKILL.md +3 -2
- package/templates/CLAUDE.md +2 -0
- package/views/_humanProps.ts +27 -0
- package/views/components.ts +16 -0
- package/views/host.ts +9 -5
- package/views/human.ts +71 -79
- package/views/protocol.ts +29 -6
- package/views/runtime.tsx +36 -45
- package/views/sample.tsx +94 -0
- package/dist/_cli/openBrowser.d.ts +0 -1
- package/dist/_cli/openBrowser.js +0 -15
- package/dist/views/_approval/ApprovalBar.d.ts +0 -20
- package/dist/views/_approval/ApprovalBar.js +0 -74
- package/dist/views/_approvalActions.d.ts +0 -18
- package/dist/views/_approvalActions.js +0 -10
- package/dist/views/index.js +0 -12
- package/views/_approval/ApprovalBar.tsx +0 -239
- package/views/_approvalActions.tsx +0 -31
- package/views/index.ts +0 -16
|
@@ -1,121 +1,29 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { jsonSchemaSchema } from "./node.js";
|
|
3
|
-
import {
|
|
4
|
-
//
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"number",
|
|
10
|
-
"integer",
|
|
11
|
-
"boolean",
|
|
12
|
-
];
|
|
13
|
-
const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
|
-
// JSON Schema の形の崩れを場所つきで返す。接続の比較(schemaFit)が扱える形だけを通す:
|
|
15
|
-
// 顧客 repo の宣言は mawaru のレビューを通らないので、壊れた型がノードに写る前に止める
|
|
16
|
-
const schemaIssues = (schema, path) => {
|
|
17
|
-
const issues = [];
|
|
18
|
-
const { type, properties, required, items } = schema;
|
|
19
|
-
if (Array.isArray(type)) {
|
|
20
|
-
issues.push({
|
|
21
|
-
path: [...path, "type"],
|
|
22
|
-
message: "配列(nullable)は使えません。任意の値は required から外してください",
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
else if (type !== undefined &&
|
|
26
|
-
!DECLARED_TYPES.includes(type)) {
|
|
27
|
-
issues.push({
|
|
28
|
-
path: [...path, "type"],
|
|
29
|
-
message: `type は ${DECLARED_TYPES.join(" / ")} のどれかにしてください(${String(type)})`,
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
else if (type === undefined &&
|
|
33
|
-
(properties !== undefined || items !== undefined)) {
|
|
34
|
-
issues.push({
|
|
35
|
-
path: [...path, "type"],
|
|
36
|
-
message: "properties / items を書くときは type も書いてください",
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
if (properties !== undefined) {
|
|
40
|
-
if (!isPlainObject(properties)) {
|
|
41
|
-
issues.push({
|
|
42
|
-
path: [...path, "properties"],
|
|
43
|
-
message: "properties はオブジェクトで書いてください",
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
for (const [key, child] of Object.entries(properties)) {
|
|
48
|
-
if (!isPlainObject(child)) {
|
|
49
|
-
issues.push({
|
|
50
|
-
path: [...path, "properties", key],
|
|
51
|
-
message: "スキーマはオブジェクトで書いてください",
|
|
52
|
-
});
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
issues.push(...schemaIssues(child, [...path, "properties", key]));
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
if (required !== undefined &&
|
|
60
|
-
!(Array.isArray(required) && required.every((r) => typeof r === "string"))) {
|
|
61
|
-
issues.push({
|
|
62
|
-
path: [...path, "required"],
|
|
63
|
-
message: "required は文字列の配列で書いてください",
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
if (items !== undefined) {
|
|
67
|
-
if (!isPlainObject(items)) {
|
|
68
|
-
issues.push({
|
|
69
|
-
path: [...path, "items"],
|
|
70
|
-
message: "items はスキーマ(オブジェクト)で書いてください",
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
issues.push(...schemaIssues(items, [...path, "items"]));
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return issues;
|
|
78
|
-
};
|
|
79
|
-
// 宣言に書く1つの型(検査つき)
|
|
80
|
-
const checkedSchema = jsonSchemaSchema.superRefine((schema, ctx) => {
|
|
81
|
-
for (const issue of schemaIssues(schema, []))
|
|
82
|
-
ctx.addIssue({ code: "custom", ...issue });
|
|
83
|
-
});
|
|
84
|
-
const declaredPortSchema = z.union([
|
|
85
|
-
z.literal(COPY_UPSTREAM_SCHEMA),
|
|
86
|
-
checkedSchema,
|
|
87
|
-
]);
|
|
88
|
-
export const humanDeclarationSchema = z.object({
|
|
89
|
-
in: declaredPortSchema,
|
|
90
|
-
approve: declaredPortSchema,
|
|
91
|
-
reject: checkedSchema,
|
|
92
|
-
});
|
|
93
|
-
// 標準ビュー(common/standard)の宣言:上流の型をそのまま受けて出し、差し戻しは標準の指示フォーム
|
|
94
|
-
export const STANDARD_HUMAN_DECLARATION = {
|
|
95
|
-
in: COPY_UPSTREAM_SCHEMA,
|
|
96
|
-
approve: COPY_UPSTREAM_SCHEMA,
|
|
97
|
-
reject: instructionSchema(),
|
|
98
|
-
};
|
|
99
|
-
// ---- 顧客 repo の nodes/human/<dir>/config.json ----
|
|
100
|
-
// 形は AI の config.json に揃える(inputs の値は { schema }、outputs の値は素の JSON Schema)。
|
|
101
|
-
// 口は in / approve / reject 固定。省略した口は STANDARD_HUMAN_DECLARATION と同じ扱い
|
|
2
|
+
import { jsonSchemaSchema, portKeySchema } from "./node.js";
|
|
3
|
+
import { kindPortsViolation } from "./port-spec.js";
|
|
4
|
+
// 顧客 repo の nodes/human/<dir>/config.json(human ノードの実装の入出力の宣言)。
|
|
5
|
+
// program / AI の config.json と同じく、エディタがディレクトリを選んだときに inputs / outputs から
|
|
6
|
+
// ports を起こし、ポートの編集は保存時にここへ書き戻される(実行時の正は ports テーブルのまま)。
|
|
7
|
+
// 形は AI に揃える(inputs の値は { schema }、outputs の値は素の JSON Schema)。
|
|
8
|
+
// 口の構成(in 1つ・approve / reject は必須・追加の出口は自由)は kind のポート構成の規則をそのまま使う
|
|
102
9
|
export const humanManifestSchema = z
|
|
103
10
|
.object({
|
|
104
|
-
inputs: z
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
outputs: z
|
|
109
|
-
.object({
|
|
110
|
-
approve: checkedSchema.optional(),
|
|
111
|
-
reject: checkedSchema.optional(),
|
|
112
|
-
})
|
|
113
|
-
.strict()
|
|
114
|
-
.optional(),
|
|
11
|
+
inputs: z.record(z.string(), z.object({ schema: jsonSchemaSchema })),
|
|
12
|
+
outputs: z.record(z.string(), jsonSchemaSchema),
|
|
13
|
+
// refs=ループ変数の定義の転写(program と同じ。エディタのグラフ保存が一方向に書く生成物)
|
|
14
|
+
refs: z.record(portKeySchema, jsonSchemaSchema).default({}),
|
|
115
15
|
})
|
|
116
|
-
.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
16
|
+
.superRefine((manifest, ctx) => {
|
|
17
|
+
const violation = kindPortsViolation("human", [
|
|
18
|
+
...Object.keys(manifest.inputs).map((key) => ({
|
|
19
|
+
io: "in",
|
|
20
|
+
key,
|
|
21
|
+
})),
|
|
22
|
+
...Object.keys(manifest.outputs).map((key) => ({
|
|
23
|
+
io: "out",
|
|
24
|
+
key,
|
|
25
|
+
})),
|
|
26
|
+
]);
|
|
27
|
+
if (violation)
|
|
28
|
+
ctx.addIssue({ code: "custom", message: violation });
|
|
121
29
|
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import type { HumanDeclaration } from "./human-manifest.js";
|
|
3
2
|
import type { GraphPort, JsonSchema } from "./node.js";
|
|
4
3
|
export declare const UNDECLARED_SCHEMA: JsonSchema;
|
|
5
4
|
export declare const isUndeclaredSchema: (schema: JsonSchema) => boolean;
|
|
@@ -57,15 +56,13 @@ type PropagationNode = {
|
|
|
57
56
|
kind: string;
|
|
58
57
|
ports: GraphPort[];
|
|
59
58
|
human?: {
|
|
60
|
-
|
|
59
|
+
human_dir?: string | null;
|
|
61
60
|
} | null;
|
|
62
61
|
};
|
|
63
62
|
type PortLink = {
|
|
64
63
|
from_port_id: string;
|
|
65
64
|
to_port_id: string;
|
|
66
65
|
};
|
|
67
|
-
export declare const COPY_UPSTREAM_SCHEMA = "T";
|
|
68
|
-
export type DeclaredPortSchema = JsonSchema | typeof COPY_UPSTREAM_SCHEMA;
|
|
69
66
|
export declare const resolvePortSchemas: <N extends PropagationNode>(nodes: N[], connections: PortLink[]) => Map<string, JsonSchema>;
|
|
70
67
|
export declare const resolveFlexiblePorts: (nodes: PropagationNode[], connections: PortLink[]) => Set<string>;
|
|
71
68
|
export declare const connectionAccepted: (from: JsonSchema, to: Pick<GraphPort, "schema" | "flexible">) => boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
// ポートの kind 別型システム(docs/tasks/done/ポートのkind別仕様.md)。
|
|
3
|
-
// 型の決まり方は2分類:宣言(program / ai の in・out
|
|
3
|
+
// 型の決まり方は2分類:宣言(program / ai と、実装(nodes/human/<dir>/)を指す human の in・out)/
|
|
4
4
|
// 導出(それ以外。接続元から伝播し、結果は ports.schema にキャッシュする)。
|
|
5
5
|
// mawaru がコードで型を固定するポートは無い
|
|
6
6
|
// (docs/tasks/done/ポート型固定の撤去とセッション再開のポート属性化.md)
|
|
@@ -222,9 +222,10 @@ export const kindPortsViolation = (kind, ports) => {
|
|
|
222
222
|
// ---- 型伝播(導出ポートの解決) ----
|
|
223
223
|
// 接続の両端は同じスキーマになる(値入力フォームのSchemaForm統一.md)。
|
|
224
224
|
// wait は素通し(パラメトリック)ノード:in は接続元の out、out は in と同型。
|
|
225
|
-
// human は実装(nodes/human
|
|
226
|
-
//
|
|
227
|
-
// program / ai
|
|
225
|
+
// human は実装(nodes/human/<dir>/)を指していれば program / ai と同じ宣言ノード、指していなければ
|
|
226
|
+
// (標準の承認画面)wait と同じ素通し(approve は in と同型、reject は標準の差し戻し instructionSchema)。
|
|
227
|
+
// 宣言 out(program / ai / 実装を指す human)は触らない。
|
|
228
|
+
// program / ai(と実装を指す human)の in は**全ての口が**接続鏡映(sticky):接続元 out が具体型のときだけ
|
|
228
229
|
// 上書きし、未接続・型割れ・未宣言は現スキーマ(宣言)を維持する(鏡映結果は repo の
|
|
229
230
|
// config.json へも書き戻される。docs/tasks/done/ポート編集のrepo書き戻し同期.md)
|
|
230
231
|
// ---- 型としての同一性(schemaTypeKey) ----
|
|
@@ -273,9 +274,6 @@ export const mergeInboundSchemas = (schemas) => {
|
|
|
273
274
|
// 流入の具体型が2種類以上あるか(=同じポートに違う型を流し込んでいる)
|
|
274
275
|
export const hasInboundConflict = (schemas) => new Set(schemas.filter((s) => !isUndeclaredSchema(s)).map(schemaTypeKey))
|
|
275
276
|
.size > 1;
|
|
276
|
-
// 「上流の out をそのままコピーする」(in)/「in と同型」(approve)を表す指示。
|
|
277
|
-
// 承認ビューのカタログ(@mawaru/common)の COPY_UPSTREAM と同じ値
|
|
278
|
-
export const COPY_UPSTREAM_SCHEMA = "T";
|
|
279
277
|
// end も導出ノード(各 in は上流から、out main は未宣言)。個別処理は
|
|
280
278
|
// resolvePortSchemas 内で行うが、初期スキーマを未宣言にするためここに含める。
|
|
281
279
|
// start も導出ノード:out main は接続先の in から逆向きに転写し、in は out と同型
|
|
@@ -284,9 +282,14 @@ export const COPY_UPSTREAM_SCHEMA = "T";
|
|
|
284
282
|
const DERIVED_KINDS = new Set(["wait", "human", "end", "start", "join"]);
|
|
285
283
|
// in を接続鏡映(sticky)するノード:接続元 out が具体型のときだけ上書き
|
|
286
284
|
const MIRROR_KINDS = new Set(["program", "ai"]);
|
|
287
|
-
|
|
285
|
+
// 実装(nodes/human/<dir>/)を指す human は program / ai と同じ(in は鏡映・out は宣言。
|
|
286
|
+
// 型は config.json へ書き戻される)。指していない human は導出(標準の承認画面の素通し)
|
|
287
|
+
const isMirrorNode = (node) => MIRROR_KINDS.has(node.kind) ||
|
|
288
|
+
(node.kind === "human" && Boolean(node.human?.human_dir));
|
|
289
|
+
const isDerivedNode = (node) => DERIVED_KINDS.has(node.kind) && !isMirrorNode(node);
|
|
290
|
+
const isDerivedPort = (node, port) => isDerivedNode(node) &&
|
|
288
291
|
(port.io === "in" ||
|
|
289
|
-
(kind !== "human" && port.key === "main") ||
|
|
292
|
+
(node.kind !== "human" && port.key === "main") ||
|
|
290
293
|
port.key === "approve" ||
|
|
291
294
|
port.key === "reject");
|
|
292
295
|
// port_id → 解決済みスキーマ。導出は接続を遡って固定点まで反復し、
|
|
@@ -300,7 +303,7 @@ export const resolvePortSchemas = (nodes, connections) => {
|
|
|
300
303
|
for (const node of nodes) {
|
|
301
304
|
for (const port of node.ports) {
|
|
302
305
|
kindByPortId.set(port.id, node.kind);
|
|
303
|
-
resolved.set(port.id, isDerivedPort(node
|
|
306
|
+
resolved.set(port.id, isDerivedPort(node, port) ? UNDECLARED_SCHEMA : port.schema);
|
|
304
307
|
}
|
|
305
308
|
}
|
|
306
309
|
// in ポートへの流入(差し戻しの fan-in があり得る。1本のときだけ型を採用)
|
|
@@ -373,10 +376,10 @@ export const resolvePortSchemas = (nodes, connections) => {
|
|
|
373
376
|
}
|
|
374
377
|
continue;
|
|
375
378
|
}
|
|
376
|
-
const isMirror =
|
|
377
|
-
if (!
|
|
379
|
+
const isMirror = isMirrorNode(node);
|
|
380
|
+
if (!isDerivedNode(node) && !isMirror)
|
|
378
381
|
continue;
|
|
379
|
-
// 鏡映(program / ai):全ての in が対象。具体型が流れてきたときだけ上書きする
|
|
382
|
+
// 鏡映(program / ai / 実装を指す human):全ての in が対象。具体型が流れてきたときだけ上書きする
|
|
380
383
|
// (宣言を未宣言で潰さない)。ai の in はどの口も対等(instruction の特別扱いは無い)
|
|
381
384
|
if (isMirror) {
|
|
382
385
|
for (const port of node.ports) {
|
|
@@ -392,34 +395,22 @@ export const resolvePortSchemas = (nodes, connections) => {
|
|
|
392
395
|
}
|
|
393
396
|
continue;
|
|
394
397
|
}
|
|
395
|
-
// 導出(wait / human):in
|
|
398
|
+
// 導出(wait / 実装を指さない human):in は上流から、out(main・approve)は in と同型。
|
|
399
|
+
// human の reject は標準の承認画面の差し戻し(指示+参考ファイル)。
|
|
400
|
+
// 上流の out は書き換えない(逆伝播すると fan-out 先の型割れが config.json へ書き戻され、
|
|
401
|
+
// 元の出力宣言を黙って壊す。ズレは接続の型エラーで見せる)
|
|
396
402
|
const inPort = node.ports.find((p) => p.io === "in");
|
|
397
403
|
if (!inPort)
|
|
398
404
|
continue;
|
|
399
405
|
const sources = inbound.get(inPort.id) ?? [];
|
|
400
406
|
const inSchema = mergeInboundSchemas(sources.map((id) => resolved.get(id) ?? UNDECLARED_SCHEMA));
|
|
401
|
-
|
|
402
|
-
// "T" は「上流の out をコピー」(in)/「in と同型」(approve)。reject は具体型の宣言
|
|
403
|
-
// のみで、宣言が無ければ未宣言のまま(mawaru が型を焼き込むことはしない)。
|
|
404
|
-
// 上流の out は書き換えない:逆伝播すると fan-out 先のビューが食い違ったとき
|
|
405
|
-
// 型割れが config.json へ write-through され、元の出力宣言を黙って壊す。
|
|
406
|
-
// ズレは接続の型エラーで見せてユーザーの [修正する] で直す
|
|
407
|
-
const declared = node.kind === "human" ? node.human?.declaration : undefined;
|
|
408
|
-
const effectiveIn = declared && declared.in !== COPY_UPSTREAM_SCHEMA
|
|
409
|
-
? declared.in
|
|
410
|
-
: inSchema;
|
|
411
|
-
assign(inPort.id, effectiveIn);
|
|
407
|
+
assign(inPort.id, inSchema);
|
|
412
408
|
for (const port of node.ports) {
|
|
413
|
-
if (port.io !== "out" || !isDerivedPort(node
|
|
409
|
+
if (port.io !== "out" || !isDerivedPort(node, port))
|
|
414
410
|
continue;
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
assign(port.id, declared && declared.approve !== COPY_UPSTREAM_SCHEMA
|
|
421
|
-
? declared.approve
|
|
422
|
-
: effectiveIn);
|
|
411
|
+
assign(port.id, node.kind === "human" && port.key === "reject"
|
|
412
|
+
? instructionSchema()
|
|
413
|
+
: inSchema);
|
|
423
414
|
}
|
|
424
415
|
}
|
|
425
416
|
if (!changed)
|
package/dist/cli.js
CHANGED
|
@@ -19,8 +19,7 @@ const USAGE = `使い方: mawaru <command> [引数]
|
|
|
19
19
|
session resume <step URL>
|
|
20
20
|
AI ノードのセッションを手元に復元して CLI(claude / codex)の resume で再開する
|
|
21
21
|
preview <種類>/<名前> [--case <ケース名>] [--surface running|completed] [--channel <#名前|ID>]
|
|
22
|
-
push せずに手元のファイルで確かめる(
|
|
23
|
-
slack:views/slack/ のビューで cases.ts のケースを Slack に送る)`;
|
|
22
|
+
views/ のビューを push せずに手元のファイルで確かめる(slack:cases.ts のケースを Slack に送る)`;
|
|
24
23
|
const [command, ...rest] = process.argv.slice(2);
|
|
25
24
|
// init / typegen / validate は repo ルートを任意で受ける(省略時はカレント)
|
|
26
25
|
const root = rest[0] ?? process.cwd();
|
package/dist/typegen.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type HumanManifest } from "./_schemas/human-manifest.js";
|
|
2
2
|
import type { JsonSchema } from "./_schemas/node.js";
|
|
3
3
|
import { type ProgramManifest } from "./_schemas/program-manifest.js";
|
|
4
4
|
export declare const CONFIG_HASH_PREFIX = "// mawaru:config-hash=";
|
|
5
5
|
export declare const configHash: (rawConfig: string) => string;
|
|
6
6
|
export declare const tsTypeFromJsonSchema: (schema: JsonSchema, indent?: number, fileAs?: string) => string;
|
|
7
7
|
export declare const renderTypesFile: (manifest: ProgramManifest, rawConfig: string) => string;
|
|
8
|
-
export declare const
|
|
8
|
+
export declare const renderHumanTypesFile: (manifest: HumanManifest, rawConfig: string) => string;
|
|
9
9
|
export type TypegenResult = {
|
|
10
10
|
written: string[];
|
|
11
11
|
errors: {
|
package/dist/typegen.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { REFS_INPUT_KEY } from "./_schemas/graph.js";
|
|
5
|
-
import {
|
|
5
|
+
import { humanManifestSchema, } from "./_schemas/human-manifest.js";
|
|
6
6
|
import { programManifestSchema, } from "./_schemas/program-manifest.js";
|
|
7
7
|
// 生成ファイルに刻む config.json 内容のハッシュ。validate が drift(config を変えて
|
|
8
8
|
// typegen し忘れ)の検出に使う
|
|
@@ -171,15 +171,18 @@ export const renderTypesFile = (manifest, rawConfig) => {
|
|
|
171
171
|
"",
|
|
172
172
|
].join("\n");
|
|
173
173
|
};
|
|
174
|
-
// human ノードの実装(nodes/human/<dir>/config.json
|
|
175
|
-
//
|
|
174
|
+
// human ノードの実装(nodes/human/<dir>/config.json)の型。program と同じ Input / Outputs / Output / Refs を作り、
|
|
175
|
+
// defineHuman<Input, Output, Refs>(({ input, out, ctx }) => …) に使う。
|
|
176
176
|
// file は参照(FileRef)のまま届く(program の handler と違って実体化しない)
|
|
177
|
-
export const
|
|
178
|
-
const
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
|
|
177
|
+
export const renderHumanTypesFile = (manifest, rawConfig) => {
|
|
178
|
+
const inputType = tsTypeFromJsonSchema(manifest.inputs.in?.schema ?? {}, 0, "FileRef");
|
|
179
|
+
const outputEntries = Object.entries(manifest.outputs).map(([key, schema]) => ` ${propertyKey(key)}: ${tsTypeFromJsonSchema(schema, 1, "FileRef")}`);
|
|
180
|
+
// refs=ループ変数の袋(program と同じ。予約キー input は run の起動入力)
|
|
181
|
+
const refEntries = [
|
|
182
|
+
` ${propertyKey(REFS_INPUT_KEY)}: unknown`,
|
|
183
|
+
...Object.entries(manifest.refs).map(([key, schema]) => ` ${propertyKey(key)}: ${tsTypeFromJsonSchema(schema, 1, "FileRef")}`),
|
|
184
|
+
];
|
|
185
|
+
const usesFileRef = [inputType, ...outputEntries, ...refEntries].some((t) => t.includes("FileRef"));
|
|
183
186
|
return [
|
|
184
187
|
"// このファイルは mawaru typegen が config.json から自動生成した(手で編集しない)",
|
|
185
188
|
`${CONFIG_HASH_PREFIX}${configHash(rawConfig)}`,
|
|
@@ -187,18 +190,18 @@ export const renderApprovalViewTypesFile = (declaration, rawConfig) => {
|
|
|
187
190
|
...(usesFileRef
|
|
188
191
|
? ['import type { FileRef } from "@mawaru/sdk/human"', ""]
|
|
189
192
|
: []),
|
|
190
|
-
|
|
191
|
-
|
|
193
|
+
`export type Input = ${inputType}`,
|
|
194
|
+
"",
|
|
195
|
+
`export type Outputs = {\n${outputEntries.join("\n")}\n}`,
|
|
192
196
|
"",
|
|
193
|
-
"//
|
|
194
|
-
|
|
197
|
+
"// out に渡す値:ちょうど1つの出口ポートのキーで渡す { <出口キー>: 値 }",
|
|
198
|
+
outputUnionType(Object.keys(manifest.outputs)),
|
|
195
199
|
"",
|
|
196
|
-
|
|
197
|
-
`export type Reject = ${rejectType}`,
|
|
200
|
+
`export type Refs = {\n${refEntries.join("\n")}\n}`,
|
|
198
201
|
"",
|
|
199
202
|
].join("\n");
|
|
200
203
|
};
|
|
201
|
-
// nodes/human/ 直下の各 human
|
|
204
|
+
// nodes/human/ 直下の各 human ノードの実装の config.json から types.d.ts を生成する(program と同じ走査)
|
|
202
205
|
const runHumanTypegen = (root) => {
|
|
203
206
|
const viewsDir = join(root, "nodes", "human");
|
|
204
207
|
const written = [];
|
|
@@ -206,11 +209,8 @@ const runHumanTypegen = (root) => {
|
|
|
206
209
|
if (!existsSync(viewsDir))
|
|
207
210
|
return { written, errors };
|
|
208
211
|
for (const entry of readdirSync(viewsDir, { withFileTypes: true })) {
|
|
209
|
-
if (!entry.isDirectory()
|
|
210
|
-
entry.name.startsWith("_") ||
|
|
211
|
-
entry.name.startsWith(".")) {
|
|
212
|
+
if (!entry.isDirectory())
|
|
212
213
|
continue;
|
|
213
|
-
}
|
|
214
214
|
const dir = `nodes/human/${entry.name}`;
|
|
215
215
|
const configPath = join(viewsDir, entry.name, "config.json");
|
|
216
216
|
if (!existsSync(configPath)) {
|
|
@@ -235,7 +235,7 @@ const runHumanTypegen = (root) => {
|
|
|
235
235
|
continue;
|
|
236
236
|
}
|
|
237
237
|
const typesPath = join(viewsDir, entry.name, "types.d.ts");
|
|
238
|
-
writeFileSync(typesPath,
|
|
238
|
+
writeFileSync(typesPath, renderHumanTypesFile(config.data, rawConfig));
|
|
239
239
|
written.push(typesPath);
|
|
240
240
|
}
|
|
241
241
|
return { written, errors };
|
package/dist/validate.js
CHANGED
|
@@ -102,19 +102,15 @@ const validateProgram = (programsDir, dir, root) => {
|
|
|
102
102
|
checkTypegenDrift(programDir, rawConfig, path, issues);
|
|
103
103
|
return issues;
|
|
104
104
|
};
|
|
105
|
-
// human ノードの実装(nodes/human/<dir>/):config.json
|
|
106
|
-
// `_` 始まりは部品の置き場なので見ない
|
|
105
|
+
// human ノードの実装(nodes/human/<dir>/):config.json・index.tsx・types.d.ts の drift(program と同じ検査)
|
|
107
106
|
const validateHumans = (root) => {
|
|
108
107
|
const issues = [];
|
|
109
108
|
const viewsDir = join(root, "nodes", "human");
|
|
110
109
|
if (!existsSync(viewsDir))
|
|
111
110
|
return issues;
|
|
112
111
|
for (const entry of readdirSync(viewsDir, { withFileTypes: true })) {
|
|
113
|
-
if (!entry.isDirectory()
|
|
114
|
-
entry.name.startsWith("_") ||
|
|
115
|
-
entry.name.startsWith(".")) {
|
|
112
|
+
if (!entry.isDirectory())
|
|
116
113
|
continue;
|
|
117
|
-
}
|
|
118
114
|
const viewDir = join(viewsDir, entry.name);
|
|
119
115
|
const path = relative(root, viewDir);
|
|
120
116
|
if (!existsSync(join(viewDir, "index.tsx"))) {
|
|
@@ -141,14 +137,20 @@ const validateHumans = (root) => {
|
|
|
141
137
|
const config = humanManifestSchema.safeParse(parsed);
|
|
142
138
|
if (!config.success) {
|
|
143
139
|
for (const issue of config.error.issues) {
|
|
140
|
+
const at = issue.path.length > 0 ? `${issue.path.join(".")}: ` : "";
|
|
144
141
|
issues.push({
|
|
145
142
|
level: "error",
|
|
146
143
|
path: `${path}/config.json`,
|
|
147
|
-
message: `${
|
|
144
|
+
message: `${at}${issue.message}`,
|
|
148
145
|
});
|
|
149
146
|
}
|
|
150
147
|
continue;
|
|
151
148
|
}
|
|
149
|
+
checkPortSchemas(Object.fromEntries(Object.entries(config.data.inputs).map(([key, value]) => [
|
|
150
|
+
key,
|
|
151
|
+
value.schema,
|
|
152
|
+
])), "inputs", `${path}/config.json`, issues);
|
|
153
|
+
checkPortSchemas(config.data.outputs, "outputs", `${path}/config.json`, issues);
|
|
152
154
|
if (existsSync(join(viewDir, "types.d.ts"))) {
|
|
153
155
|
checkTypegenDrift(viewDir, rawConfig, path, issues);
|
|
154
156
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FileRef } from "./_parts/fileRef.js";
|
|
2
|
+
import type { HumanProps } from "./human.js";
|
|
3
|
+
import type { HumanViewData } from "./protocol.js";
|
|
4
|
+
export type HumanOps = {
|
|
5
|
+
out: (output: Record<string, unknown>) => Promise<void>;
|
|
6
|
+
fetchFileUrl: (file: FileRef) => Promise<string>;
|
|
7
|
+
uploadFile: (file: File) => Promise<FileRef>;
|
|
8
|
+
writeText: (text: string) => Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
export declare const humanPropsOf: (data: HumanViewData, ops: HumanOps) => HumanProps;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const humanPropsOf = (data, ops) => ({
|
|
2
|
+
input: data.input,
|
|
3
|
+
out: ops.out,
|
|
4
|
+
ctx: {
|
|
5
|
+
input: { schema: data.inputSchema, fetchFileUrl: ops.fetchFileUrl },
|
|
6
|
+
output: { schema: data.outputSchema, uploadFile: ops.uploadFile },
|
|
7
|
+
clipboard: { writeText: ops.writeText },
|
|
8
|
+
refs: (data.refs ?? {}),
|
|
9
|
+
step: { isAssignee: data.isAssignee, editable: data.editable },
|
|
10
|
+
},
|
|
11
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// 共通の部品(`@mawaru/sdk/components`)。human ノードの約束(`@mawaru/sdk/human`)には依存しない。
|
|
2
|
+
// mawaru の画面と顧客 repo の human ノードの実装が同じ部品を使う(mawaru が顧客 repo の実装をまとめるとき、
|
|
3
|
+
// mawaru の今の版のこの部品を同梱する)。ファイルの表示・アップロードは、mawaru の画面ではホストの口、
|
|
4
|
+
// sandbox iframe の中では mawaru の承認画面への頼みごととして裏で差し込まれる(_bridge.tsx)。
|
|
5
|
+
// **公開 API**:部品の props は後方互換でしか変えない
|
|
6
|
+
export { documentCardStyle } from "./_parts/DocumentCard.js";
|
|
7
|
+
export { FilePreview } from "./_parts/FilePreview.js";
|
|
8
|
+
export { isFileRef } from "./_parts/fileRef.js";
|
|
9
|
+
export { isUrl, Linkify, linkify } from "./_parts/Linkify.js";
|
|
10
|
+
export { isLongText, Markdown, useAutoGrow } from "./_parts/Markdown.js";
|
|
11
|
+
export { asFormSchema, SchemaForm, } from "./_parts/SchemaForm.js";
|
|
12
|
+
export { NARROW_WIDTH, useNarrow } from "./_parts/useNarrow.js";
|
package/dist/views/host.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
2
|
+
export { useViewBridge, type ViewBridge, ViewBridgeProvider, } from "./_bridge.js";
|
|
3
|
+
export { type HumanOps, humanPropsOf } from "./_humanProps.js";
|
|
4
4
|
export * from "./protocol.js";
|
|
5
5
|
export declare const viewToHostSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
6
6
|
tag: z.ZodLiteral<"mawaru:view">;
|
|
@@ -13,6 +13,11 @@ export declare const viewToHostSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
13
13
|
tag: z.ZodLiteral<"mawaru:view">;
|
|
14
14
|
type: z.ZodLiteral<"request">;
|
|
15
15
|
id: z.ZodNumber;
|
|
16
|
-
method: z.
|
|
16
|
+
method: z.ZodEnum<{
|
|
17
|
+
out: "out";
|
|
18
|
+
fetchFileUrl: "fetchFileUrl";
|
|
19
|
+
uploadFile: "uploadFile";
|
|
20
|
+
writeText: "writeText";
|
|
21
|
+
}>;
|
|
17
22
|
args: z.ZodUnknown;
|
|
18
23
|
}, z.core.$strip>], "type">;
|
package/dist/views/host.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { VIEW_MESSAGE_TAG } from "./protocol.js";
|
|
3
|
-
export { ApprovalActionsProvider } from "./_approvalActions.js";
|
|
4
3
|
// ホスト(mawaru の画面)だけが使うもの(公開 API ではない):sandbox iframe からのメッセージを確かめる形
|
|
5
|
-
// (protocol.ts の型と同じ。iframe
|
|
6
|
-
//
|
|
7
|
-
export { ViewBridgeProvider } from "./_bridge.js";
|
|
4
|
+
// (protocol.ts の型と同じ。iframe の中は信用できないコードなので、形を確かめてから使う)、
|
|
5
|
+
// 部品が裏で使う口を差す提供部品、human ノードの実装に渡す props の組み立て(標準の承認画面も同じものを使う)
|
|
6
|
+
export { useViewBridge, ViewBridgeProvider, } from "./_bridge.js";
|
|
7
|
+
export { humanPropsOf } from "./_humanProps.js";
|
|
8
8
|
export * from "./protocol.js";
|
|
9
9
|
const tag = z.literal(VIEW_MESSAGE_TAG);
|
|
10
10
|
export const viewToHostSchema = z.discriminatedUnion("type", [
|
|
@@ -14,7 +14,7 @@ export const viewToHostSchema = z.discriminatedUnion("type", [
|
|
|
14
14
|
tag,
|
|
15
15
|
type: z.literal("request"),
|
|
16
16
|
id: z.number().int(),
|
|
17
|
-
method: z.
|
|
17
|
+
method: z.enum(["out", "fetchFileUrl", "uploadFile", "writeText"]),
|
|
18
18
|
args: z.unknown(),
|
|
19
19
|
}),
|
|
20
20
|
]);
|