@mawaru/sdk 0.38.0 → 0.40.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/ai-manifest.test.ts +8 -0
- package/_schemas/ai-manifest.ts +2 -0
- package/_schemas/graph.test.ts +51 -9
- package/_schemas/graph.ts +23 -8
- package/_schemas/human-manifest.test.ts +129 -0
- package/_schemas/human-manifest.ts +158 -0
- package/_schemas/node.ts +4 -0
- package/_schemas/port-spec.test.ts +164 -40
- package/_schemas/port-spec.ts +77 -26
- package/dist/_cli/login.js +1 -15
- package/dist/_cli/openBrowser.d.ts +1 -0
- package/dist/_cli/openBrowser.js +15 -0
- package/dist/_cli/previewApi.d.ts +1 -1
- package/dist/_cli/previewCommand.d.ts +8 -0
- package/dist/_cli/previewCommand.js +95 -37
- package/dist/_cli/previewFiles.d.ts +2 -1
- package/dist/_cli/previewFiles.js +42 -19
- package/dist/_cli/previewSlack.d.ts +1 -1
- package/dist/_cli/previewSlack.js +1 -1
- package/dist/_schemas/ai-manifest.d.ts +3 -0
- package/dist/_schemas/ai-manifest.js +2 -0
- package/dist/_schemas/graph.d.ts +23 -3
- package/dist/_schemas/graph.js +20 -8
- package/dist/_schemas/human-manifest.d.ts +27 -0
- package/dist/_schemas/human-manifest.js +121 -0
- package/dist/_schemas/node.d.ts +1 -0
- package/dist/_schemas/node.js +4 -0
- package/dist/_schemas/port-spec.d.ts +8 -9
- package/dist/_schemas/port-spec.js +64 -9
- package/dist/cli.js +3 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/init.js +22 -3
- package/dist/typegen.d.ts +3 -1
- package/dist/typegen.js +96 -14
- package/dist/validate.js +55 -0
- package/dist/views/_approval/ApprovalBar.d.ts +20 -0
- package/dist/views/_approval/ApprovalBar.js +74 -0
- package/dist/views/_approvalActions.d.ts +18 -0
- package/dist/views/_approvalActions.js +10 -0
- package/dist/views/_bridge.d.ts +12 -0
- package/dist/views/_bridge.js +10 -0
- package/dist/views/_parts/DocumentCard.d.ts +2 -0
- package/dist/views/_parts/DocumentCard.js +16 -0
- package/dist/views/_parts/FilePreview.d.ts +5 -0
- package/dist/views/_parts/FilePreview.js +101 -0
- package/dist/views/_parts/Linkify.d.ts +7 -0
- package/dist/views/_parts/Linkify.js +28 -0
- package/dist/views/_parts/Markdown.d.ts +44 -0
- package/dist/views/_parts/Markdown.js +313 -0
- package/dist/views/_parts/SchemaForm.d.ts +19 -0
- package/dist/views/_parts/SchemaForm.js +310 -0
- package/dist/views/_parts/fileRef.d.ts +7 -0
- package/dist/views/_parts/fileRef.js +11 -0
- package/dist/views/_parts/useNarrow.d.ts +5 -0
- package/dist/views/_parts/useNarrow.js +23 -0
- package/dist/views/host.d.ts +18 -0
- package/dist/views/host.js +20 -0
- package/dist/views/human.d.ts +73 -0
- package/dist/views/human.js +6 -0
- package/dist/views/index.d.ts +8 -0
- package/dist/views/index.js +12 -0
- package/dist/views/protocol.d.ts +34 -0
- package/dist/views/protocol.js +5 -0
- package/dist/views/runtime.d.ts +3 -0
- package/dist/views/runtime.js +86 -0
- package/dist/{slack-view.d.ts → views/slack.d.ts} +1 -1
- package/docs/development.md +45 -5
- package/index.ts +1 -0
- package/package.json +36 -4
- package/skills/create-loop/SKILL.md +6 -3
- package/views/_approval/ApprovalBar.tsx +239 -0
- package/views/_approvalActions.tsx +31 -0
- package/views/_bridge.tsx +36 -0
- package/views/_parts/DocumentCard.ts +18 -0
- package/views/_parts/FilePreview.tsx +180 -0
- package/views/_parts/Linkify.test.ts +40 -0
- package/views/_parts/Linkify.tsx +40 -0
- package/views/_parts/Markdown.test.ts +131 -0
- package/views/_parts/Markdown.tsx +472 -0
- package/views/_parts/SchemaForm.tsx +742 -0
- package/views/_parts/fileRef.ts +21 -0
- package/views/_parts/useNarrow.ts +24 -0
- package/views/host.ts +23 -0
- package/views/human.ts +91 -0
- package/views/index.ts +16 -0
- package/views/protocol.ts +40 -0
- package/views/runtime.tsx +132 -0
- package/views/slack.test.ts +40 -0
- package/{slack-view.ts → views/slack.ts} +3 -3
- /package/dist/{slack-view.js → views/slack.js} +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest"
|
|
2
|
+
import type { HumanDeclaration } from "./human-manifest.js"
|
|
2
3
|
import type { GraphPort, JsonSchema } from "./node.js"
|
|
3
4
|
import {
|
|
4
5
|
arrayOf,
|
|
6
|
+
connectionAccepted,
|
|
5
7
|
dataFormatOf,
|
|
6
8
|
dataSchemaOf,
|
|
7
9
|
fileSchema,
|
|
@@ -12,6 +14,7 @@ import {
|
|
|
12
14
|
kindPortsViolation,
|
|
13
15
|
mergeInboundSchemas,
|
|
14
16
|
propagateDerivedPorts,
|
|
17
|
+
resolveFlexiblePorts,
|
|
15
18
|
resolvePortSchemas,
|
|
16
19
|
schemaFit,
|
|
17
20
|
schemaTypeKey,
|
|
@@ -151,6 +154,15 @@ describe("kindPortsViolation(kind 別ポート構成)", () => {
|
|
|
151
154
|
})
|
|
152
155
|
})
|
|
153
156
|
|
|
157
|
+
// human ノードに実装の宣言の写し(node_humans.declaration)を持たせる
|
|
158
|
+
const withViewDeclaration = <N extends { kind: string }>(
|
|
159
|
+
nodes: N[],
|
|
160
|
+
declaration: HumanDeclaration,
|
|
161
|
+
) =>
|
|
162
|
+
nodes.map((node) =>
|
|
163
|
+
node.kind === "human" ? { ...node, human: { declaration } } : node,
|
|
164
|
+
)
|
|
165
|
+
|
|
154
166
|
describe("型伝播(導出ポートの解決)", () => {
|
|
155
167
|
it.each([
|
|
156
168
|
"notion_task",
|
|
@@ -171,13 +183,14 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
171
183
|
extra,
|
|
172
184
|
],
|
|
173
185
|
}
|
|
174
|
-
const resolved = resolvePortSchemas(
|
|
175
|
-
|
|
186
|
+
const resolved = resolvePortSchemas(
|
|
187
|
+
withViewDeclaration([human], {
|
|
176
188
|
in: { type: "string" },
|
|
177
189
|
approve: { type: "string" },
|
|
178
190
|
reject: { type: "string" },
|
|
179
191
|
}),
|
|
180
|
-
|
|
192
|
+
[],
|
|
193
|
+
)
|
|
181
194
|
expect(resolved.get(extra.id)).toEqual(extra.schema)
|
|
182
195
|
})
|
|
183
196
|
|
|
@@ -189,7 +202,7 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
189
202
|
}
|
|
190
203
|
|
|
191
204
|
// human の in / approve / reject はビューの宣言をそのまま転写する(上流の kind は見ない)。
|
|
192
|
-
// sdk
|
|
205
|
+
// 宣言はノードの写し(declaration)を読む(sdk はカタログを知らない)
|
|
193
206
|
// → docs/tasks/done/ポート型固定の撤去とセッション再開のポート属性化.md
|
|
194
207
|
it("ai → human 直結(コピー宣言):in も approve も上流の out と同型、reject はビューの宣言型", () => {
|
|
195
208
|
const aiOut = port("out", "main", declaredOut)
|
|
@@ -207,15 +220,12 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
207
220
|
ports: [humanIn, approve, reject],
|
|
208
221
|
}
|
|
209
222
|
const resolved = resolvePortSchemas(
|
|
210
|
-
[ai, human],
|
|
223
|
+
withViewDeclaration([ai, human], {
|
|
224
|
+
in: "T",
|
|
225
|
+
approve: "T",
|
|
226
|
+
reject: instructionSchema(),
|
|
227
|
+
}),
|
|
211
228
|
[{ from_port_id: aiOut.id, to_port_id: humanIn.id }],
|
|
212
|
-
{
|
|
213
|
-
viewPortSchemasOf: () => ({
|
|
214
|
-
in: "T",
|
|
215
|
-
approve: "T",
|
|
216
|
-
reject: instructionSchema(),
|
|
217
|
-
}),
|
|
218
|
-
},
|
|
219
229
|
)
|
|
220
230
|
// 上流が AI でも中身を取り出さない:AI の出力形式がそのまま下流へ流れる
|
|
221
231
|
expect(resolved.get(humanIn.id)).toEqual(declaredOut)
|
|
@@ -223,7 +233,7 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
223
233
|
expect(resolved.get(reject.id)).toEqual(instructionSchema())
|
|
224
234
|
})
|
|
225
235
|
|
|
226
|
-
describe("
|
|
236
|
+
describe("実装の宣言を human のポートへ転写する", () => {
|
|
227
237
|
const viewIn = {
|
|
228
238
|
type: "object",
|
|
229
239
|
properties: {
|
|
@@ -272,15 +282,12 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
272
282
|
declaredOut,
|
|
273
283
|
)
|
|
274
284
|
const resolved = resolvePortSchemas(
|
|
275
|
-
[upstream, human],
|
|
285
|
+
withViewDeclaration([upstream, human], {
|
|
286
|
+
in: viewIn,
|
|
287
|
+
approve: viewOut,
|
|
288
|
+
reject: viewReject,
|
|
289
|
+
}),
|
|
276
290
|
[{ from_port_id: out.id, to_port_id: humanIn.id }],
|
|
277
|
-
{
|
|
278
|
-
viewPortSchemasOf: () => ({
|
|
279
|
-
in: viewIn,
|
|
280
|
-
approve: viewOut,
|
|
281
|
-
reject: viewReject,
|
|
282
|
-
}),
|
|
283
|
-
},
|
|
284
291
|
)
|
|
285
292
|
expect(resolved.get(humanIn.id)).toEqual(viewIn)
|
|
286
293
|
expect(resolved.get(approve.id)).toEqual(viewOut)
|
|
@@ -291,15 +298,12 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
291
298
|
it("上流の out は書き換えない(逆伝播しない)", () => {
|
|
292
299
|
const { upstream, human, out, humanIn } = build("ai", declaredOut)
|
|
293
300
|
const resolved = resolvePortSchemas(
|
|
294
|
-
[upstream, human],
|
|
301
|
+
withViewDeclaration([upstream, human], {
|
|
302
|
+
in: viewIn,
|
|
303
|
+
approve: viewOut,
|
|
304
|
+
reject: viewReject,
|
|
305
|
+
}),
|
|
295
306
|
[{ from_port_id: out.id, to_port_id: humanIn.id }],
|
|
296
|
-
{
|
|
297
|
-
viewPortSchemasOf: () => ({
|
|
298
|
-
in: viewIn,
|
|
299
|
-
approve: viewOut,
|
|
300
|
-
reject: viewReject,
|
|
301
|
-
}),
|
|
302
|
-
},
|
|
303
307
|
)
|
|
304
308
|
expect(resolved.get(out.id)).toEqual(declaredOut)
|
|
305
309
|
})
|
|
@@ -310,15 +314,12 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
310
314
|
declaredOut,
|
|
311
315
|
)
|
|
312
316
|
const resolved = resolvePortSchemas(
|
|
313
|
-
[upstream, human],
|
|
317
|
+
withViewDeclaration([upstream, human], {
|
|
318
|
+
in: "T",
|
|
319
|
+
approve: "T",
|
|
320
|
+
reject: viewReject,
|
|
321
|
+
}),
|
|
314
322
|
[{ from_port_id: out.id, to_port_id: humanIn.id }],
|
|
315
|
-
{
|
|
316
|
-
viewPortSchemasOf: () => ({
|
|
317
|
-
in: "T",
|
|
318
|
-
approve: "T",
|
|
319
|
-
reject: viewReject,
|
|
320
|
-
}),
|
|
321
|
-
},
|
|
322
323
|
)
|
|
323
324
|
expect(resolved.get(humanIn.id)).toEqual(declaredOut)
|
|
324
325
|
expect(resolved.get(approve.id)).toEqual(declaredOut)
|
|
@@ -333,13 +334,14 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
333
334
|
kind: "human",
|
|
334
335
|
ports: [humanIn, approve, reject],
|
|
335
336
|
}
|
|
336
|
-
const resolved = resolvePortSchemas(
|
|
337
|
-
|
|
337
|
+
const resolved = resolvePortSchemas(
|
|
338
|
+
withViewDeclaration([human], {
|
|
338
339
|
in: viewIn,
|
|
339
340
|
approve: viewOut,
|
|
340
341
|
reject: viewReject,
|
|
341
342
|
}),
|
|
342
|
-
|
|
343
|
+
[],
|
|
344
|
+
)
|
|
343
345
|
expect(resolved.get(humanIn.id)).toEqual(viewIn)
|
|
344
346
|
expect(resolved.get(approve.id)).toEqual(viewOut)
|
|
345
347
|
expect(resolved.get(reject.id)).toEqual(viewReject)
|
|
@@ -661,6 +663,128 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
661
663
|
expect(resolved.get(start.outMain.id)).toEqual(UNDECLARED_SCHEMA)
|
|
662
664
|
})
|
|
663
665
|
|
|
666
|
+
// flexible:AI の in の型属性。宣言(推奨型)を維持し、接続の型を問わない。
|
|
667
|
+
// start へは型と同じ経路で導出される(AIノード入力のflexible属性(任意の型で呼べるループ).md)
|
|
668
|
+
describe("flexible(推奨型を維持して任意の型を受ける in)", () => {
|
|
669
|
+
const flexibleAi = (declared: JsonSchema) => {
|
|
670
|
+
const inPort: GraphPort = {
|
|
671
|
+
...port("in", "in", declared),
|
|
672
|
+
flexible: true,
|
|
673
|
+
}
|
|
674
|
+
return {
|
|
675
|
+
node: { id: uuid(), kind: "ai", ports: [inPort, port("out", "main")] },
|
|
676
|
+
inPort,
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
const recommended = {
|
|
680
|
+
type: "object",
|
|
681
|
+
properties: { prompt: { type: "string" } },
|
|
682
|
+
required: ["prompt"],
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
it("flexible な in は接続元の具体型で上書きしない(宣言=推奨型を維持)", () => {
|
|
686
|
+
const program = programNode({ type: "string" })
|
|
687
|
+
const progOut = program.node.ports.find(
|
|
688
|
+
(p) => p.io === "out",
|
|
689
|
+
) as GraphPort
|
|
690
|
+
progOut.schema = declaredOut
|
|
691
|
+
const ai = flexibleAi(recommended)
|
|
692
|
+
const resolved = resolvePortSchemas(
|
|
693
|
+
[program.node, ai.node],
|
|
694
|
+
[{ from_port_id: progOut.id, to_port_id: ai.inPort.id }],
|
|
695
|
+
)
|
|
696
|
+
expect(resolved.get(ai.inPort.id)).toEqual(recommended)
|
|
697
|
+
})
|
|
698
|
+
|
|
699
|
+
it("flexible な in は型の割れた fan-in でも推奨型を維持する", () => {
|
|
700
|
+
const a = programNode({ type: "string" })
|
|
701
|
+
const aOut = a.node.ports.find((p) => p.io === "out") as GraphPort
|
|
702
|
+
aOut.schema = { type: "string" }
|
|
703
|
+
const b = programNode({ type: "string" })
|
|
704
|
+
const bOut = b.node.ports.find((p) => p.io === "out") as GraphPort
|
|
705
|
+
bOut.schema = { type: "number" }
|
|
706
|
+
const ai = flexibleAi(recommended)
|
|
707
|
+
const resolved = resolvePortSchemas(
|
|
708
|
+
[a.node, b.node, ai.node],
|
|
709
|
+
[
|
|
710
|
+
{ from_port_id: aOut.id, to_port_id: ai.inPort.id },
|
|
711
|
+
{ from_port_id: bOut.id, to_port_id: ai.inPort.id },
|
|
712
|
+
],
|
|
713
|
+
)
|
|
714
|
+
expect(resolved.get(ai.inPort.id)).toEqual(recommended)
|
|
715
|
+
})
|
|
716
|
+
|
|
717
|
+
it("start → AI(flexible) 直結:start の in / out main が推奨型で flexible になる", () => {
|
|
718
|
+
const start = startNode()
|
|
719
|
+
const ai = flexibleAi(recommended)
|
|
720
|
+
const connections = [
|
|
721
|
+
{ from_port_id: start.outMain.id, to_port_id: ai.inPort.id },
|
|
722
|
+
]
|
|
723
|
+
expect(resolveFlexiblePorts([start.node, ai.node], connections)).toEqual(
|
|
724
|
+
new Set([start.inPort.id, start.outMain.id]),
|
|
725
|
+
)
|
|
726
|
+
const propagated = propagateDerivedPorts(
|
|
727
|
+
[start.node, ai.node],
|
|
728
|
+
connections,
|
|
729
|
+
)[0]
|
|
730
|
+
if (!propagated) throw new Error("start not propagated")
|
|
731
|
+
for (const p of propagated.ports) {
|
|
732
|
+
expect(p.schema).toEqual(recommended)
|
|
733
|
+
expect(p.flexible).toBe(true)
|
|
734
|
+
}
|
|
735
|
+
})
|
|
736
|
+
|
|
737
|
+
it("start → [AI(flexible), program]:非 flexible の接続先が混ざれば start は flexible にならない", () => {
|
|
738
|
+
const start = startNode()
|
|
739
|
+
const ai = flexibleAi(recommended)
|
|
740
|
+
const program = programNode(recommended)
|
|
741
|
+
const connections = [
|
|
742
|
+
{ from_port_id: start.outMain.id, to_port_id: ai.inPort.id },
|
|
743
|
+
{ from_port_id: start.outMain.id, to_port_id: program.inPort.id },
|
|
744
|
+
]
|
|
745
|
+
expect(
|
|
746
|
+
resolveFlexiblePorts([start.node, ai.node, program.node], connections),
|
|
747
|
+
).toEqual(new Set())
|
|
748
|
+
})
|
|
749
|
+
|
|
750
|
+
it("start:未接続なら flexible にならず、ports に残った古い flexible は落とす", () => {
|
|
751
|
+
const inPort: GraphPort = { ...port("in", "in"), flexible: true }
|
|
752
|
+
const outMain: GraphPort = { ...port("out", "main"), flexible: true }
|
|
753
|
+
const start = { id: uuid(), kind: "start", ports: [inPort, outMain] }
|
|
754
|
+
const propagated = propagateDerivedPorts([start], [])[0]
|
|
755
|
+
if (!propagated) throw new Error("start not propagated")
|
|
756
|
+
for (const p of propagated.ports) expect(p.flexible).toBeUndefined()
|
|
757
|
+
})
|
|
758
|
+
|
|
759
|
+
it("component の in(契約の焼き込みで flexible)に直結した start も flexible(推移)", () => {
|
|
760
|
+
const start = startNode()
|
|
761
|
+
const compIn: GraphPort = {
|
|
762
|
+
...port("in", "in", recommended),
|
|
763
|
+
flexible: true,
|
|
764
|
+
}
|
|
765
|
+
const component = {
|
|
766
|
+
id: uuid(),
|
|
767
|
+
kind: "component",
|
|
768
|
+
ports: [compIn, port("out", "main")],
|
|
769
|
+
}
|
|
770
|
+
expect(
|
|
771
|
+
resolveFlexiblePorts(
|
|
772
|
+
[start.node, component],
|
|
773
|
+
[{ from_port_id: start.outMain.id, to_port_id: compIn.id }],
|
|
774
|
+
),
|
|
775
|
+
).toEqual(new Set([start.inPort.id, start.outMain.id]))
|
|
776
|
+
})
|
|
777
|
+
|
|
778
|
+
it("connectionAccepted:受け側が flexible なら型を問わず可、そうでなければ isSchemaSubset", () => {
|
|
779
|
+
const to = port("in", "in", recommended)
|
|
780
|
+
expect(connectionAccepted({ type: "string" }, to)).toBe(false)
|
|
781
|
+
expect(
|
|
782
|
+
connectionAccepted({ type: "string" }, { ...to, flexible: true }),
|
|
783
|
+
).toBe(true)
|
|
784
|
+
expect(connectionAccepted(recommended, to)).toBe(true)
|
|
785
|
+
})
|
|
786
|
+
})
|
|
787
|
+
|
|
664
788
|
it("propagateDerivedPorts:schema を書き戻し、変化のないノードは同じ参照を返す", () => {
|
|
665
789
|
const progOut = port("out", "main", declaredOut)
|
|
666
790
|
const program = {
|
package/_schemas/port-spec.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { z } from "zod"
|
|
2
|
+
import type { HumanDeclaration } from "./human-manifest.js"
|
|
2
3
|
import type { GraphPort, JsonSchema, PortIo } from "./node.js"
|
|
3
4
|
|
|
4
5
|
// ポートの kind 別型システム(docs/tasks/done/ポートのkind別仕様.md)。
|
|
5
|
-
// 型の決まり方は2分類:宣言(program / ai の in・out。human
|
|
6
|
+
// 型の決まり方は2分類:宣言(program / ai の in・out。human は実装(nodes/human/)の宣言を転写)/
|
|
6
7
|
// 導出(それ以外。接続元から伝播し、結果は ports.schema にキャッシュする)。
|
|
7
8
|
// mawaru がコードで型を固定するポートは無い
|
|
8
9
|
// (docs/tasks/done/ポート型固定の撤去とセッション再開のポート属性化.md)
|
|
@@ -265,7 +266,7 @@ export const kindPortsViolation = (
|
|
|
265
266
|
// ---- 型伝播(導出ポートの解決) ----
|
|
266
267
|
// 接続の両端は同じスキーマになる(値入力フォームのSchemaForm統一.md)。
|
|
267
268
|
// wait は素通し(パラメトリック)ノード:in は接続元の out、out は in と同型。
|
|
268
|
-
// human
|
|
269
|
+
// human は実装(nodes/human/)の宣言を転写("T" は素通しと同じ挙動。reject もビューの宣言)。
|
|
269
270
|
// 宣言 out(program / ai)は触らない。
|
|
270
271
|
// program / ai の in は**全ての口が**接続鏡映(sticky):接続元 out が具体型のときだけ
|
|
271
272
|
// 上書きし、未接続・型割れ・未宣言は現スキーマ(宣言)を維持する(鏡映結果は repo の
|
|
@@ -328,6 +329,9 @@ type PropagationNode = {
|
|
|
328
329
|
id: string
|
|
329
330
|
kind: string
|
|
330
331
|
ports: GraphPort[]
|
|
332
|
+
// human のときだけ意味を持つ:実装(nodes/human/<dir>/)の宣言の写し(node_humans.declaration)。
|
|
333
|
+
// 無ければ in・approve は上流のコピー、reject は触らない
|
|
334
|
+
human?: { declaration?: HumanDeclaration } | null
|
|
331
335
|
}
|
|
332
336
|
type PortLink = { from_port_id: string; to_port_id: string }
|
|
333
337
|
|
|
@@ -338,23 +342,6 @@ export const COPY_UPSTREAM_SCHEMA = "T"
|
|
|
338
342
|
// ポートの型宣言:具体型か、上流からのコピー指示か
|
|
339
343
|
export type DeclaredPortSchema = JsonSchema | typeof COPY_UPSTREAM_SCHEMA
|
|
340
344
|
|
|
341
|
-
// 伝播のオプション。sdk は承認ビューのカタログを知らない(非公開。企業向けビューの
|
|
342
|
-
// キーが npm 公開物に載るのを避ける)ので、呼び出し元が解決して渡す
|
|
343
|
-
// → docs/tasks/done/ポート型固定の撤去とセッション再開のポート属性化.md
|
|
344
|
-
export type PropagationOptions<N> = {
|
|
345
|
-
// human の in / approve / reject に転写する型(承認ビューの宣言)。null / undefined
|
|
346
|
-
// ならその node は対象外(human 以外)。上流ノードの kind は一切見ない。
|
|
347
|
-
// reject は具体型のみ(上流からのコピー指示は意味を持たない)
|
|
348
|
-
viewPortSchemasOf?: (node: N) =>
|
|
349
|
-
| {
|
|
350
|
-
in: DeclaredPortSchema
|
|
351
|
-
approve: DeclaredPortSchema
|
|
352
|
-
reject: JsonSchema
|
|
353
|
-
}
|
|
354
|
-
| null
|
|
355
|
-
| undefined
|
|
356
|
-
}
|
|
357
|
-
|
|
358
345
|
// end も導出ノード(各 in は上流から、out main は未宣言)。個別処理は
|
|
359
346
|
// resolvePortSchemas 内で行うが、初期スキーマを未宣言にするためここに含める。
|
|
360
347
|
// start も導出ノード:out main は接続先の in から逆向きに転写し、in は out と同型
|
|
@@ -380,7 +367,6 @@ const isDerivedPort = (kind: string, port: Pick<GraphPort, "io" | "key">) =>
|
|
|
380
367
|
export const resolvePortSchemas = <N extends PropagationNode>(
|
|
381
368
|
nodes: N[],
|
|
382
369
|
connections: PortLink[],
|
|
383
|
-
options?: PropagationOptions<N>,
|
|
384
370
|
): Map<string, JsonSchema> => {
|
|
385
371
|
const resolved = new Map<string, JsonSchema>()
|
|
386
372
|
const kindByPortId = new Map<string, string>()
|
|
@@ -487,6 +473,8 @@ export const resolvePortSchemas = <N extends PropagationNode>(
|
|
|
487
473
|
if (isMirror) {
|
|
488
474
|
for (const port of node.ports) {
|
|
489
475
|
if (port.io !== "in") continue
|
|
476
|
+
// flexible な in は宣言(推奨型)を維持する(接続の型を問わない口)
|
|
477
|
+
if (port.flexible === true) continue
|
|
490
478
|
const sources = inbound.get(port.id) ?? []
|
|
491
479
|
const inSchema = mergeInboundSchemas(
|
|
492
480
|
sources.map((id) => resolved.get(id) ?? UNDECLARED_SCHEMA),
|
|
@@ -502,13 +490,14 @@ export const resolvePortSchemas = <N extends PropagationNode>(
|
|
|
502
490
|
const inSchema = mergeInboundSchemas(
|
|
503
491
|
sources.map((id) => resolved.get(id) ?? UNDECLARED_SCHEMA),
|
|
504
492
|
)
|
|
505
|
-
//
|
|
493
|
+
// 実装(nodes/human/<dir>/)の宣言を human のポートへそのまま転写する(上流ノードの kind は見ない)。
|
|
506
494
|
// "T" は「上流の out をコピー」(in)/「in と同型」(approve)。reject は具体型の宣言
|
|
507
495
|
// のみで、宣言が無ければ未宣言のまま(mawaru が型を焼き込むことはしない)。
|
|
508
496
|
// 上流の out は書き換えない:逆伝播すると fan-out 先のビューが食い違ったとき
|
|
509
497
|
// 型割れが config.json へ write-through され、元の出力宣言を黙って壊す。
|
|
510
498
|
// ズレは接続の型エラーで見せてユーザーの [修正する] で直す
|
|
511
|
-
const declared =
|
|
499
|
+
const declared =
|
|
500
|
+
node.kind === "human" ? node.human?.declaration : undefined
|
|
512
501
|
const effectiveIn =
|
|
513
502
|
declared && declared.in !== COPY_UPSTREAM_SCHEMA
|
|
514
503
|
? declared.in
|
|
@@ -535,19 +524,81 @@ export const resolvePortSchemas = <N extends PropagationNode>(
|
|
|
535
524
|
|
|
536
525
|
// ノード配列へ伝播結果を書き戻す(接続変更時のポートデータ自動更新)。
|
|
537
526
|
// schema が変わらないポート・ノードは同じ参照を返す
|
|
527
|
+
// flexible の導出:start の in / out main は、接続先の in が1つ以上あり**全て** flexible
|
|
528
|
+
// (AI の宣言 in・契約で焼き込まれた component の in)のときだけ flexible。
|
|
529
|
+
// 非 flexible の接続先(program 等)が1つでも混ざれば型どおりの入力が要るので false。
|
|
530
|
+
// 直結のみ(wait 等の素通し経由は型と同じく流さない)。戻りは flexible になる port_id の集合
|
|
531
|
+
export const resolveFlexiblePorts = (
|
|
532
|
+
nodes: PropagationNode[],
|
|
533
|
+
connections: PortLink[],
|
|
534
|
+
): Set<string> => {
|
|
535
|
+
const flexibleByPortId = new Map<string, boolean>()
|
|
536
|
+
for (const node of nodes) {
|
|
537
|
+
if (node.kind === "start") continue
|
|
538
|
+
for (const port of node.ports) {
|
|
539
|
+
if (port.io === "in")
|
|
540
|
+
flexibleByPortId.set(port.id, port.flexible === true)
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
const outbound = new Map<string, string[]>()
|
|
544
|
+
for (const conn of connections) {
|
|
545
|
+
const outs = outbound.get(conn.from_port_id) ?? []
|
|
546
|
+
outs.push(conn.to_port_id)
|
|
547
|
+
outbound.set(conn.from_port_id, outs)
|
|
548
|
+
}
|
|
549
|
+
const flexible = new Set<string>()
|
|
550
|
+
for (const node of nodes) {
|
|
551
|
+
if (node.kind !== "start") continue
|
|
552
|
+
const inPort = node.ports.find((p) => p.io === "in")
|
|
553
|
+
const outMain = node.ports.find((p) => p.io === "out" && p.key === "main")
|
|
554
|
+
if (!inPort || !outMain) continue
|
|
555
|
+
const targets = outbound.get(outMain.id) ?? []
|
|
556
|
+
if (
|
|
557
|
+
targets.length > 0 &&
|
|
558
|
+
targets.every((id) => flexibleByPortId.get(id) === true)
|
|
559
|
+
) {
|
|
560
|
+
flexible.add(inPort.id)
|
|
561
|
+
flexible.add(outMain.id)
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return flexible
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// 接続の型が受け入れられるか:受け側が flexible なら常に可、それ以外は 伝播型 ⊆ 宣言。
|
|
568
|
+
// sdk の保存契約・backend の保存時チェック・エディタの表示はすべてこれを使う
|
|
569
|
+
// (判定が散ると表示と保存がズレる)
|
|
570
|
+
export const connectionAccepted = (
|
|
571
|
+
from: JsonSchema,
|
|
572
|
+
to: Pick<GraphPort, "schema" | "flexible">,
|
|
573
|
+
): boolean => to.flexible === true || isSchemaSubset(from, to.schema)
|
|
574
|
+
|
|
538
575
|
export const propagateDerivedPorts = <N extends PropagationNode>(
|
|
539
576
|
nodes: N[],
|
|
540
577
|
connections: PortLink[],
|
|
541
|
-
options?: PropagationOptions<N>,
|
|
542
578
|
): N[] => {
|
|
543
|
-
const resolved = resolvePortSchemas(nodes, connections
|
|
579
|
+
const resolved = resolvePortSchemas(nodes, connections)
|
|
580
|
+
const flexiblePorts = resolveFlexiblePorts(nodes, connections)
|
|
544
581
|
return nodes.map((node) => {
|
|
545
582
|
let touched = false
|
|
546
583
|
const ports = node.ports.map((port) => {
|
|
547
584
|
const schema = resolved.get(port.id) ?? port.schema
|
|
548
|
-
|
|
585
|
+
// start の flexible は導出結果で置き換える(false は省略=キー無し)
|
|
586
|
+
const flexible =
|
|
587
|
+
node.kind === "start"
|
|
588
|
+
? flexiblePorts.has(port.id) || undefined
|
|
589
|
+
: port.flexible
|
|
590
|
+
if (
|
|
591
|
+
JSON.stringify(schema) === JSON.stringify(port.schema) &&
|
|
592
|
+
flexible === port.flexible
|
|
593
|
+
)
|
|
594
|
+
return port
|
|
549
595
|
touched = true
|
|
550
|
-
|
|
596
|
+
const { flexible: _dropped, ...rest } = port
|
|
597
|
+
return {
|
|
598
|
+
...rest,
|
|
599
|
+
schema,
|
|
600
|
+
...(flexible ? { flexible } : {}),
|
|
601
|
+
} as typeof port
|
|
551
602
|
})
|
|
552
603
|
return touched ? { ...node, ports } : node
|
|
553
604
|
})
|
package/dist/_cli/login.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
1
|
import { randomUUID } from "node:crypto";
|
|
3
2
|
import { createServer } from "node:http";
|
|
4
3
|
import { saveCredentials } from "./credentials.js";
|
|
4
|
+
import { openBrowser } from "./openBrowser.js";
|
|
5
5
|
// `gh auth login` と同じループバック方式(RFC 8252)。
|
|
6
6
|
// CLI が 127.0.0.1 の一時ポートで待ち受け、ブラウザで mawaru の /cli-auth を開く。
|
|
7
7
|
// 承認するとブラウザがループバック URL へ**遷移**してトークンを渡す
|
|
@@ -11,20 +11,6 @@ const page = (title, message) => `<!doctype html><meta charset="utf-8"><title>${
|
|
|
11
11
|
`<body style="font-family:system-ui;display:grid;place-items:center;height:100vh;margin:0">` +
|
|
12
12
|
`<div style="text-align:center"><h1 style="font-size:1.25rem">${title}</h1>` +
|
|
13
13
|
`<p style="color:#666">${message}</p></div></body>`;
|
|
14
|
-
// ブラウザを開く(開けなければ URL を出して手で開いてもらう。依存は足さない)
|
|
15
|
-
const openBrowser = (url) => {
|
|
16
|
-
const [command, args] = process.platform === "darwin"
|
|
17
|
-
? ["open", [url]]
|
|
18
|
-
: process.platform === "win32"
|
|
19
|
-
? ["cmd", ["/c", "start", "", url]]
|
|
20
|
-
: ["xdg-open", [url]];
|
|
21
|
-
try {
|
|
22
|
-
spawn(command, args, { stdio: "ignore", detached: true }).unref();
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
// 何もしない(URL は既に印字済み)
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
14
|
export const runLogin = async (timeoutMs = 5 * 60 * 1000) => {
|
|
29
15
|
// 別のローカルプロセスが投げ込んだ応答を受け付けないための照合値
|
|
30
16
|
const state = randomUUID();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const openBrowser: (url: string) => void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
// ブラウザを開く(開けなければ URL を出して手で開いてもらう。依存は足さない)
|
|
3
|
+
export const openBrowser = (url) => {
|
|
4
|
+
const [command, args] = process.platform === "darwin"
|
|
5
|
+
? ["open", [url]]
|
|
6
|
+
: process.platform === "win32"
|
|
7
|
+
? ["cmd", ["/c", "start", "", url]]
|
|
8
|
+
: ["xdg-open", [url]];
|
|
9
|
+
try {
|
|
10
|
+
spawn(command, args, { stdio: "ignore", detached: true }).unref();
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
// 何もしない(URL は既に印字済み)
|
|
14
|
+
}
|
|
15
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Credentials } from "./credentials.js";
|
|
2
|
-
export type MawaruApi = (method: "GET" | "POST", path: string, body?: unknown) => Promise<{
|
|
2
|
+
export type MawaruApi = (method: "GET" | "POST" | "PUT", path: string, body?: unknown) => Promise<{
|
|
3
3
|
status: number;
|
|
4
4
|
body: unknown;
|
|
5
5
|
}>;
|
|
@@ -5,14 +5,22 @@ export type PreviewOptions = {
|
|
|
5
5
|
case?: string;
|
|
6
6
|
surface?: SlackSurface;
|
|
7
7
|
channel?: string;
|
|
8
|
+
once?: boolean;
|
|
8
9
|
};
|
|
9
10
|
export declare const parsePreviewArgs: (args: string[]) => PreviewOptions;
|
|
10
11
|
type Log = (line: string) => void;
|
|
12
|
+
export type PreviewEnv = {
|
|
13
|
+
tenantId: string;
|
|
14
|
+
appUrl: string;
|
|
15
|
+
openUrl: (url: string) => void;
|
|
16
|
+
watch: (dir: string, onChange: () => Promise<void>) => Promise<void>;
|
|
17
|
+
};
|
|
11
18
|
export declare const runPreviewWith: (params: {
|
|
12
19
|
root: string;
|
|
13
20
|
options: PreviewOptions;
|
|
14
21
|
api: MawaruApi;
|
|
15
22
|
log: Log;
|
|
23
|
+
env?: Partial<PreviewEnv>;
|
|
16
24
|
}) => Promise<number>;
|
|
17
25
|
export declare const runPreview: (args: string[]) => Promise<number>;
|
|
18
26
|
export {};
|