@mawaru/sdk 0.20.0 → 0.22.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.
@@ -31,24 +31,6 @@ describe("waitConfigSchema", () => {
31
31
  }
32
32
  })
33
33
 
34
- it("until: ISO 日時(Z / オフセット)を受け付ける", () => {
35
- for (const until_at of [
36
- "2026-07-10T09:00:00.000Z",
37
- "2026-07-10T09:00:00+09:00",
38
- ]) {
39
- expect(waitConfigSchema.parse({ wait_type: "until", until_at })).toEqual({
40
- wait_type: "until",
41
- until_at,
42
- })
43
- }
44
- })
45
-
46
- it("until: 日時でない文字列を拒否する", () => {
47
- expect(() =>
48
- waitConfigSchema.parse({ wait_type: "until", until_at: "2026-07-10" }),
49
- ).toThrow()
50
- })
51
-
52
34
  it("cron: 非空の式を受け付ける(式の妥当性検証は保存時)", () => {
53
35
  expect(
54
36
  waitConfigSchema.parse({ wait_type: "cron", cron_expr: "0 9 * * 1-5" }),
@@ -62,20 +44,26 @@ describe("waitConfigSchema", () => {
62
44
  })
63
45
 
64
46
  it("種別と値カラムの取り違えを拒否する", () => {
65
- // duration に until_at だけ、など判別後の必須欠落
47
+ // duration に cron_expr だけ、など判別後の必須欠落
66
48
  expect(() =>
67
49
  waitConfigSchema.parse({
68
50
  wait_type: "duration",
69
- until_at: "2026-07-10T09:00:00.000Z",
51
+ cron_expr: "0 9 * * *",
70
52
  }),
71
53
  ).toThrow()
72
54
  expect(() =>
73
- waitConfigSchema.parse({ wait_type: "until", duration_seconds: 60 }),
55
+ waitConfigSchema.parse({ wait_type: "cron", duration_seconds: 60 }),
74
56
  ).toThrow()
75
57
  })
76
58
 
77
- it("未知の wait_type を拒否する", () => {
59
+ it("未知の wait_type を拒否する(廃止した until を含む)", () => {
78
60
  expect(() => waitConfigSchema.parse({ wait_type: "forever" })).toThrow()
61
+ expect(() =>
62
+ waitConfigSchema.parse({
63
+ wait_type: "until",
64
+ until_at: "2026-07-10T09:00:00.000Z",
65
+ }),
66
+ ).toThrow()
79
67
  })
80
68
  })
81
69
 
package/_schemas/graph.ts CHANGED
@@ -78,17 +78,15 @@ export const graphNodeKindSchema = nodeKindSchema.extract([
78
78
  export type GraphNodeKind = z.infer<typeof graphNodeKindSchema>
79
79
 
80
80
  // Wait ノードの kind 別設定(node_waits の wait_type に対応する判別ユニオン)。
81
- // duration=経過秒、until=絶対時刻(ISO)、cron=cron 式(次回発火まで1回待つ)。
81
+ // duration=経過秒、cron=cron 式(次に来る該当時刻まで1回待つ)。
82
+ // wait は到達時に fireAt を1つ決めて1回だけ発火するので、絶対時刻を1点指す種別(旧 until)は
83
+ // 繰り返し回るループの部品にならない=持たない。
82
84
  // cron 式の妥当性は保存時に backend の cron パーサで検証する(ここは非空のみ)
83
85
  export const waitConfigSchema = z.discriminatedUnion("wait_type", [
84
86
  z.object({
85
87
  wait_type: z.literal("duration"),
86
88
  duration_seconds: z.number().int().positive(),
87
89
  }),
88
- z.object({
89
- wait_type: z.literal("until"),
90
- until_at: z.iso.datetime({ offset: true }),
91
- }),
92
90
  z.object({
93
91
  wait_type: z.literal("cron"),
94
92
  cron_expr: z.string().min(1),
@@ -272,7 +270,8 @@ export const graphNodeSchema = z.object({
272
270
  icon: nodeIconSchema.optional(),
273
271
  // Slack ビュー(全 kind 共通の任意設定。承認待ち・完了メッセージの見せ方)。
274
272
  // human の view と同じ「不透明なキー」で、実在・可視性の検証は保存時に mawaru 側が行う
275
- // (カタログは非公開。@mawaru/common の slack-view.ts)。省略時は従来表示。
273
+ // (カタログは非公開。@mawaru/common の slack-view.ts)。
274
+ // 省略=承認待ちは承認ボタンだけ・完了は ✅ の1行(データは出さない)。
276
275
  // ポートの型には一切影響しない
277
276
  slack_view: z.string().min(1).optional(),
278
277
  // kind 別設定(自 kind のときだけ意味を持つ。省略時は保存側がデフォルトを補う)
@@ -9,6 +9,7 @@ export declare const recordedCwds: (transcript: string) => {
9
9
  all: string[];
10
10
  last: string | null;
11
11
  };
12
+ export declare const workspaceRepoName: (cwds: string[]) => string | null;
12
13
  export declare const findProjectDir: (cwd: string) => string | null;
13
14
  export type TranscriptRef = {
14
15
  url: string;
@@ -50,6 +50,19 @@ export const recordedCwds = (transcript) => {
50
50
  }
51
51
  return { all, last };
52
52
  };
53
+ // ランナー上の cwd は /home/runner/work/<実行repo>/<実行repo>/workspace/<対象repo>/... の形。
54
+ // エージェントが実際に触ったのは setup.sh が workspace/ 配下に clone した**対象 repo** なので、
55
+ // そのセグメントだけを人間への手がかりとして取り出す(フルパスはローカルでは解決しないので見せない)
56
+ export const workspaceRepoName = (cwds) => {
57
+ for (const cwd of cwds) {
58
+ const segments = cwd.split("/");
59
+ const index = segments.indexOf("workspace");
60
+ const name = index >= 0 ? segments[index + 1] : undefined;
61
+ if (name)
62
+ return name;
63
+ }
64
+ return null;
65
+ };
53
66
  const projectsRoot = () => join(process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), ".claude"), "projects");
54
67
  // 置き先のディレクトリを **規則を再実装せず観測で決める**(agent-runner の
55
68
  // _helpers/session.ts と同じ方針)。~/.claude/projects/*/*.jsonl の cwd を読み、
@@ -1,3 +1 @@
1
- export declare const runSessionResume: (stepUrl: string | undefined, options: {
2
- yes: boolean;
3
- }) => Promise<number>;
1
+ export declare const runSessionResume: (stepUrl: string | undefined) => Promise<number>;
@@ -1,25 +1,22 @@
1
- import { createInterface } from "node:readline/promises";
1
+ import { homedir } from "node:os";
2
+ import { dirname } from "node:path";
2
3
  import { requireCredentials } from "./credentials.js";
3
- import { downloadTranscript, fetchTranscriptRef, parseStepUrl, placeTranscript, recordedCwds, spawnClaudeResume, } from "./resumeSession.js";
4
- const confirm = async (question) => {
5
- const rl = createInterface({ input: process.stdin, output: process.stdout });
6
- try {
7
- const answer = await rl.question(`${question} [y/N] `);
8
- return /^y(es)?$/i.test(answer.trim());
9
- }
10
- finally {
11
- rl.close();
12
- }
4
+ import { downloadTranscript, fetchTranscriptRef, parseStepUrl, placeTranscript, recordedCwds, spawnClaudeResume, workspaceRepoName, } from "./resumeSession.js";
5
+ // ホーム配下は ~ に畳む(置き先は必ずホーム配下なので実質いつも効く)
6
+ const tilde = (path) => {
7
+ const home = homedir();
8
+ return path.startsWith(home) ? `~${path.slice(home.length)}` : path;
13
9
  };
14
- // `mawaru session resume <step URL> [--yes]`
10
+ // `mawaru session resume <step URL>`
15
11
  //
16
- // 取得 作業ディレクトリの確認 → 配置 → `claude --resume`。
17
- // **どこに cd すべきかは mawaru には分からない**(AI ノードの実行 repo と、エージェントが
18
- // 実際に触った repo は別物で、後者は setup.sh workspace/ 配下に clone した対象 repo)。
19
- // なので推定せず、記録された cwd をそのまま見せて人間に判断させる
20
- export const runSessionResume = async (stepUrl, options) => {
12
+ // **確認は取らない。** 書くのは ~/.claude/projects/<現在地>/<sessionId>.jsonl の新規1ファイルだけで
13
+ // repo には触れないので、場所を間違えても別プロジェクトの履歴に混ざるだけ=やり直せる。
14
+ // **どこに cd すべきかは mawaru には分からない**(AI ノードの実行 repo と、エージェントが実際に
15
+ // 触った repo は別物で、後者は setup.sh が workspace/ 配下に clone した対象 repo)ので推定して
16
+ // 移動もしない。手がかりとして対象 repo 名だけ見せ、現在地と食い違うときに警告する
17
+ export const runSessionResume = async (stepUrl) => {
21
18
  if (!stepUrl) {
22
- console.error("使い方: mawaru session resume <step URL> [--yes]\n" +
19
+ console.error("使い方: mawaru session resume <step URL>\n" +
23
20
  "run 詳細の AI ノードにあるコピーボタンで URL を取得できます。");
24
21
  return 1;
25
22
  }
@@ -28,28 +25,19 @@ export const runSessionResume = async (stepUrl, options) => {
28
25
  const credentials = requireCredentials();
29
26
  const transcriptRef = await fetchTranscriptRef(credentials, ref);
30
27
  const transcript = await downloadTranscript(transcriptRef.url);
31
- console.log(`取得: セッション ${transcriptRef.session_id}`);
28
+ console.log(`取得: セッション ${transcriptRef.session_id.slice(0, 8)}`);
32
29
  if (transcriptRef.redacted) {
33
30
  console.log(`(退避時に secret を ${transcriptRef.redacted} 箇所マスクしています)`);
34
31
  }
35
- const { all, last } = recordedCwds(transcript);
36
32
  const cwd = process.cwd();
37
- if (all.length > 0) {
38
- console.log("このセッションの作業ディレクトリ:");
39
- for (const dir of all) {
40
- console.log(` ${dir}${dir === last ? "(最後に居た場所)" : ""}`);
33
+ const repo = workspaceRepoName(recordedCwds(transcript).all);
34
+ if (repo) {
35
+ console.log(`実行時の作業対象: ${repo}(ランナー上の workspace/${repo})`);
36
+ if (!cwd.includes(repo)) {
37
+ console.log(`⚠ 現在地に ${repo} が含まれません。別 repo で再開しようとしているかもしれません(このまま続けます)`);
41
38
  }
42
39
  }
43
- console.log(`現在地:\n ${cwd}`);
44
- if (!all.includes(cwd)) {
45
- console.log("※ 記録された作業ディレクトリとは違います。記録上の絶対パスはここでは解決しません" +
46
- "(エージェントは読めないパスに当たれば自分で見つけ直します)。");
47
- }
48
- if (!options.yes && !(await confirm("続けますか?"))) {
49
- console.log("中止しました。");
50
- return 1;
51
- }
52
40
  const file = placeTranscript(cwd, transcriptRef.session_id, transcript);
53
- console.log(`配置: ${file}\n再開します…`);
41
+ console.log(`復元先: ${tilde(dirname(file))}\n再開します…`);
54
42
  return await spawnClaudeResume(transcriptRef.session_id);
55
43
  };
@@ -18,20 +18,17 @@ export declare const editableNodeKindSchema: z.ZodEnum<{
18
18
  export type EditableNodeKind = z.infer<typeof editableNodeKindSchema>;
19
19
  export declare const graphNodeKindSchema: z.ZodEnum<{
20
20
  start: "start";
21
- end: "end";
22
21
  ai: "ai";
23
22
  human: "human";
24
23
  program: "program";
25
24
  wait: "wait";
25
+ end: "end";
26
26
  component: "component";
27
27
  }>;
28
28
  export type GraphNodeKind = z.infer<typeof graphNodeKindSchema>;
29
29
  export declare const waitConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
30
30
  wait_type: z.ZodLiteral<"duration">;
31
31
  duration_seconds: z.ZodNumber;
32
- }, z.core.$strip>, z.ZodObject<{
33
- wait_type: z.ZodLiteral<"until">;
34
- until_at: z.ZodISODateTime;
35
32
  }, z.core.$strip>, z.ZodObject<{
36
33
  wait_type: z.ZodLiteral<"cron">;
37
34
  cron_expr: z.ZodString;
@@ -58,8 +55,8 @@ export declare const humanConfigSchema: z.ZodObject<{
58
55
  round_robin: "round_robin";
59
56
  }>>;
60
57
  approval_mode: z.ZodDefault<z.ZodEnum<{
61
- all: "all";
62
58
  any: "any";
59
+ all: "all";
63
60
  }>>;
64
61
  units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>>;
65
62
  }, z.core.$strip>;
@@ -131,11 +128,11 @@ export declare const graphNodeSchema: z.ZodObject<{
131
128
  id: z.ZodUUID;
132
129
  kind: z.ZodEnum<{
133
130
  start: "start";
134
- end: "end";
135
131
  ai: "ai";
136
132
  human: "human";
137
133
  program: "program";
138
134
  wait: "wait";
135
+ end: "end";
139
136
  component: "component";
140
137
  }>;
141
138
  name: z.ZodString;
@@ -177,9 +174,6 @@ export declare const graphNodeSchema: z.ZodObject<{
177
174
  wait: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
178
175
  wait_type: z.ZodLiteral<"duration">;
179
176
  duration_seconds: z.ZodNumber;
180
- }, z.core.$strip>, z.ZodObject<{
181
- wait_type: z.ZodLiteral<"until">;
182
- until_at: z.ZodISODateTime;
183
177
  }, z.core.$strip>, z.ZodObject<{
184
178
  wait_type: z.ZodLiteral<"cron">;
185
179
  cron_expr: z.ZodString;
@@ -200,8 +194,8 @@ export declare const graphNodeSchema: z.ZodObject<{
200
194
  round_robin: "round_robin";
201
195
  }>>;
202
196
  approval_mode: z.ZodDefault<z.ZodEnum<{
203
- all: "all";
204
197
  any: "any";
198
+ all: "all";
205
199
  }>>;
206
200
  units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>>;
207
201
  }, z.core.$strip>;
@@ -231,11 +225,11 @@ export declare const saveGraphBodySchema: z.ZodObject<{
231
225
  id: z.ZodUUID;
232
226
  kind: z.ZodEnum<{
233
227
  start: "start";
234
- end: "end";
235
228
  ai: "ai";
236
229
  human: "human";
237
230
  program: "program";
238
231
  wait: "wait";
232
+ end: "end";
239
233
  component: "component";
240
234
  }>;
241
235
  name: z.ZodString;
@@ -277,9 +271,6 @@ export declare const saveGraphBodySchema: z.ZodObject<{
277
271
  wait: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
278
272
  wait_type: z.ZodLiteral<"duration">;
279
273
  duration_seconds: z.ZodNumber;
280
- }, z.core.$strip>, z.ZodObject<{
281
- wait_type: z.ZodLiteral<"until">;
282
- until_at: z.ZodISODateTime;
283
274
  }, z.core.$strip>, z.ZodObject<{
284
275
  wait_type: z.ZodLiteral<"cron">;
285
276
  cron_expr: z.ZodString;
@@ -300,8 +291,8 @@ export declare const saveGraphBodySchema: z.ZodObject<{
300
291
  round_robin: "round_robin";
301
292
  }>>;
302
293
  approval_mode: z.ZodDefault<z.ZodEnum<{
303
- all: "all";
304
294
  any: "any";
295
+ all: "all";
305
296
  }>>;
306
297
  units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>>;
307
298
  }, z.core.$strip>;
@@ -53,17 +53,15 @@ export const graphNodeKindSchema = nodeKindSchema.extract([
53
53
  "component",
54
54
  ]);
55
55
  // Wait ノードの kind 別設定(node_waits の wait_type に対応する判別ユニオン)。
56
- // duration=経過秒、until=絶対時刻(ISO)、cron=cron 式(次回発火まで1回待つ)。
56
+ // duration=経過秒、cron=cron 式(次に来る該当時刻まで1回待つ)。
57
+ // wait は到達時に fireAt を1つ決めて1回だけ発火するので、絶対時刻を1点指す種別(旧 until)は
58
+ // 繰り返し回るループの部品にならない=持たない。
57
59
  // cron 式の妥当性は保存時に backend の cron パーサで検証する(ここは非空のみ)
58
60
  export const waitConfigSchema = z.discriminatedUnion("wait_type", [
59
61
  z.object({
60
62
  wait_type: z.literal("duration"),
61
63
  duration_seconds: z.number().int().positive(),
62
64
  }),
63
- z.object({
64
- wait_type: z.literal("until"),
65
- until_at: z.iso.datetime({ offset: true }),
66
- }),
67
65
  z.object({
68
66
  wait_type: z.literal("cron"),
69
67
  cron_expr: z.string().min(1),
@@ -216,7 +214,8 @@ export const graphNodeSchema = z.object({
216
214
  icon: nodeIconSchema.optional(),
217
215
  // Slack ビュー(全 kind 共通の任意設定。承認待ち・完了メッセージの見せ方)。
218
216
  // human の view と同じ「不透明なキー」で、実在・可視性の検証は保存時に mawaru 側が行う
219
- // (カタログは非公開。@mawaru/common の slack-view.ts)。省略時は従来表示。
217
+ // (カタログは非公開。@mawaru/common の slack-view.ts)。
218
+ // 省略=承認待ちは承認ボタンだけ・完了は ✅ の1行(データは出さない)。
220
219
  // ポートの型には一切影響しない
221
220
  slack_view: z.string().min(1).optional(),
222
221
  // kind 別設定(自 kind のときだけ意味を持つ。省略時は保存側がデフォルトを補う)
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  export declare const hookEventSchema: z.ZodEnum<{
3
- input: "input";
4
3
  output: "output";
4
+ input: "input";
5
5
  signal: "signal";
6
6
  }>;
7
7
  export type HookEvent = z.infer<typeof hookEventSchema>;
@@ -9,8 +9,8 @@ export declare const hookManifestSchema: z.ZodObject<{
9
9
  name: z.ZodOptional<z.ZodString>;
10
10
  description: z.ZodOptional<z.ZodString>;
11
11
  on: z.ZodArray<z.ZodEnum<{
12
- input: "input";
13
12
  output: "output";
13
+ input: "input";
14
14
  signal: "signal";
15
15
  }>>;
16
16
  env: z.ZodDefault<z.ZodArray<z.ZodString>>;
@@ -1,12 +1,12 @@
1
1
  import { z } from "zod";
2
2
  export declare const nodeKindSchema: z.ZodEnum<{
3
3
  start: "start";
4
- end: "end";
5
4
  ai: "ai";
6
5
  human: "human";
7
6
  program: "program";
8
7
  guardrail: "guardrail";
9
8
  wait: "wait";
9
+ end: "end";
10
10
  component: "component";
11
11
  }>;
12
12
  export type NodeKind = z.infer<typeof nodeKindSchema>;
package/dist/cli.js CHANGED
@@ -15,7 +15,7 @@ const USAGE = `使い方: mawaru <command> [引数]
15
15
  validate リポジトリ規約を検証する(error があれば exit 1。CI 向け)
16
16
  login ブラウザで承認して mawaru にログインする(~/.mawaru/credentials.json)
17
17
  logout 保存した資格情報を消す
18
- session resume <step URL> [--yes]
18
+ session resume <step URL>
19
19
  AI ノードのセッションを手元に復元して claude --resume で再開する`;
20
20
  const [command, ...rest] = process.argv.slice(2);
21
21
  // init / typegen / validate は repo ルートを任意で受ける(省略時はカレント)
@@ -81,10 +81,10 @@ switch (command) {
81
81
  case "session": {
82
82
  const [subcommand, ...args] = rest;
83
83
  if (subcommand !== "resume") {
84
- fail("使い方: mawaru session resume <step URL> [--yes]");
84
+ fail("使い方: mawaru session resume <step URL>");
85
85
  }
86
86
  try {
87
- const code = await runSessionResume(args.find((a) => !a.startsWith("-")), { yes: args.includes("--yes") || args.includes("-y") });
87
+ const code = await runSessionResume(args.find((a) => !a.startsWith("-")));
88
88
  process.exit(code);
89
89
  }
90
90
  catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mawaru/sdk",
3
- "version": "0.20.0",
3
+ "version": "0.22.0",
4
4
  "description": "mawaru 実行 repo の開発 SDK。契約スキーマ(config.json 規約・ループ graph API)の正 + typegen / validate CLI",
5
5
  "type": "module",
6
6
  "bin": {