svamp-cli 0.2.194 → 0.2.196

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.
Files changed (26) hide show
  1. package/bin/skills/crew/SKILL.md +24 -1
  2. package/dist/{agentCommands-u3lACoAL.mjs → agentCommands-Epy2LP4k.mjs} +5 -5
  3. package/dist/{auth-CA8v11FJ.mjs → auth-BVAW-c8L.mjs} +1 -1
  4. package/dist/cli.mjs +60 -60
  5. package/dist/{commands-DRYKMdYK.mjs → commands-D3KJdH3r.mjs} +5 -3
  6. package/dist/{commands-CZD9sTwX.mjs → commands-D8ad_1Aa.mjs} +1 -1
  7. package/dist/{commands-Cj1ipwaC.mjs → commands-DYxBgrYh.mjs} +2 -2
  8. package/dist/{commands-DlKKUqVK.mjs → commands-DsYzp1Pc.mjs} +1 -1
  9. package/dist/{commands-CnOAurhc.mjs → commands-Dx6gz9Dr.mjs} +5 -5
  10. package/dist/{commands-CqnF0mFt.mjs → commands-i3J0d8xS.mjs} +137 -49
  11. package/dist/{commands-UDDf7PQt.mjs → commands-yNPBtne1.mjs} +1 -1
  12. package/dist/{fleet-CPdmiNDX.mjs → fleet-DwAy8i7Z.mjs} +1 -1
  13. package/dist/{frpc-CBDwOpZZ.mjs → frpc-DeD80HI1.mjs} +1 -1
  14. package/dist/{headlessCli-CFj2Gxe0.mjs → headlessCli-Cw8ZR1H7.mjs} +2 -2
  15. package/dist/{httpServer-Cf54pQ77.mjs → httpServer-B1KVQJfm.mjs} +38 -0
  16. package/dist/index.mjs +1 -1
  17. package/dist/{package-C9H0l19h.mjs → package-CpNcSvfm.mjs} +2 -2
  18. package/dist/{rpc-6ftZ30co.mjs → rpc-Blk-caU2.mjs} +1 -1
  19. package/dist/{rpc-BHrZL52o.mjs → rpc-CRpXmlRy.mjs} +6 -2
  20. package/dist/{run-6hsamvmy.mjs → run-CBWkZyBD.mjs} +1 -1
  21. package/dist/{run-Dn-FEr9q.mjs → run-Cxq7C5mA.mjs} +329 -39
  22. package/dist/{scheduler-BtltL3kB.mjs → scheduler-ro49WXPg.mjs} +1 -1
  23. package/dist/{serveCommands-DEGssQo6.mjs → serveCommands-5tBepki5.mjs} +5 -5
  24. package/dist/{serveManager-DwO36v9t.mjs → serveManager-D0edqFb9.mjs} +2 -2
  25. package/dist/{sideband-CjLQLnk1.mjs → sideband-p3YKDEQ0.mjs} +1 -1
  26. package/package.json +2 -2
@@ -58,7 +58,7 @@ async function serviceExpose(args) {
58
58
  process.exit(1);
59
59
  }
60
60
  if (foreground) {
61
- const { runFrpcTunnel } = await import('./frpc-CBDwOpZZ.mjs');
61
+ const { runFrpcTunnel } = await import('./frpc-DeD80HI1.mjs');
62
62
  await runFrpcTunnel(name, ports, void 0, {
63
63
  group,
64
64
  groupKey,
@@ -68,7 +68,7 @@ async function serviceExpose(args) {
68
68
  });
69
69
  return;
70
70
  }
71
- const { connectAndGetMachine } = await import('./commands-DlKKUqVK.mjs');
71
+ const { connectAndGetMachine } = await import('./commands-DsYzp1Pc.mjs');
72
72
  const { server, machine } = await connectAndGetMachine();
73
73
  try {
74
74
  const status = await machine.tunnelStart({
@@ -123,7 +123,7 @@ async function serviceServe(args) {
123
123
  };
124
124
  process.on("SIGINT", cleanup);
125
125
  process.on("SIGTERM", cleanup);
126
- const { runFrpcTunnel } = await import('./frpc-CBDwOpZZ.mjs');
126
+ const { runFrpcTunnel } = await import('./frpc-DeD80HI1.mjs');
127
127
  await runFrpcTunnel(name, [caddyPort]);
128
128
  } catch (err) {
129
129
  console.error(`Error serving directory: ${err.message}`);
@@ -132,7 +132,7 @@ async function serviceServe(args) {
132
132
  }
133
133
  async function serviceList(_args) {
134
134
  try {
135
- const { connectAndGetMachine } = await import('./commands-DlKKUqVK.mjs');
135
+ const { connectAndGetMachine } = await import('./commands-DsYzp1Pc.mjs');
136
136
  const { server, machine } = await connectAndGetMachine();
137
137
  try {
138
138
  const tunnels = await machine.tunnelList({});
@@ -172,7 +172,7 @@ async function serviceDelete(args) {
172
172
  process.exit(1);
173
173
  }
174
174
  try {
175
- const { connectAndGetMachine } = await import('./commands-DlKKUqVK.mjs');
175
+ const { connectAndGetMachine } = await import('./commands-DsYzp1Pc.mjs');
176
176
  const { server, machine } = await connectAndGetMachine();
177
177
  try {
178
178
  await machine.tunnelStop({ name });
@@ -1,8 +1,8 @@
1
1
  import { existsSync } from 'node:fs';
2
- import { connectAndGetMachine, resolveSessionId, createWorktree, connectAndResolveSession } from './commands-DlKKUqVK.mjs';
2
+ import { resolve } from 'node:path';
3
+ import { connectAndGetMachine, resolveSessionId, createWorktree, connectAndResolveSession } from './commands-DsYzp1Pc.mjs';
3
4
  import { execSync } from 'node:child_process';
4
- import { u as updateIssue, o as addComment, p as addIssue, x as shortId } from './run-Dn-FEr9q.mjs';
5
- import 'node:path';
5
+ import { u as updateIssue, o as addComment, p as addIssue, y as shortId } from './run-Cxq7C5mA.mjs';
6
6
  import 'node:os';
7
7
  import 'os';
8
8
  import 'fs/promises';
@@ -126,6 +126,35 @@ function projectRootFromWorktree(worktreePath) {
126
126
  return worktreePath.slice(0, idx);
127
127
  }
128
128
 
129
+ function isStandaloneChild(crew) {
130
+ return !!crew && crew.standalone === true;
131
+ }
132
+ function worktreeCrewMeta(feature, branch, worktreePath, baseBranch) {
133
+ return { role: "feature", feature, branch, worktreePath, baseBranch };
134
+ }
135
+ function standaloneCrewMeta(feature, directory) {
136
+ return { role: "feature", feature, standalone: true, directory };
137
+ }
138
+ function childDirectory(crew, sessionDir) {
139
+ return crew?.worktreePath || crew?.directory || sessionDir || "";
140
+ }
141
+ function standaloneBrief(directory, leadShort, brief) {
142
+ return `You are a crew STANDALONE child working directly in folder \`${directory}\` (no git worktree, no feature branch), reporting to lead \`${leadShort}\`.
143
+
144
+ Feature brief:
145
+ ${brief}
146
+
147
+ Work in this folder. Commit there yourself if it is a git repo. Report milestones with \`svamp feature report\`, and when done run \`svamp feature done\` \u2014 there is no branch to merge, so your lead just detaches & closes you.`;
148
+ }
149
+ function worktreeBrief(branch, base, leadShort, brief) {
150
+ return `You are a crew feature child on branch \`${branch}\` (base \`${base}\`), reporting to lead \`${leadShort}\`.
151
+
152
+ Feature brief:
153
+ ${brief}
154
+
155
+ Work in this worktree. Keep tests green, report milestones with \`svamp feature report\`, and when done run \`svamp feature done\` so your lead can review & merge. After merge you'll be closed.`;
156
+ }
157
+
129
158
  function requireLead(explicit) {
130
159
  const id = explicit || process.env.SVAMP_SESSION_ID;
131
160
  if (!id) {
@@ -166,22 +195,41 @@ async function featureStart(brief, opts = {}) {
166
195
  try {
167
196
  const sessions = await machine.listSessions();
168
197
  const lead = resolveSessionId(sessions, leadId);
169
- const projectRoot = opts.directory || lead.directory;
170
- if (!projectRoot) {
171
- console.error("Could not determine the lead project directory.");
172
- process.exit(1);
173
- }
174
- const base = opts.baseBranch || currentBranch(projectRoot) || "main";
175
- let wt;
176
- try {
177
- wt = createWorktree(projectRoot);
178
- } catch (e) {
179
- console.error(`Failed to create worktree: ${e.message}`);
180
- process.exit(1);
198
+ const standalone = !!opts.noWorktree;
199
+ let childDir;
200
+ let base = "";
201
+ let wt = null;
202
+ if (standalone) {
203
+ const dir = opts.directory;
204
+ if (!dir) {
205
+ console.error("--no-worktree requires --dir <folder> (the existing folder to run the child in).");
206
+ process.exit(1);
207
+ }
208
+ childDir = resolve(dir);
209
+ if (!existsSync(childDir)) {
210
+ console.error(`Folder not found: ${childDir}`);
211
+ process.exit(1);
212
+ }
213
+ console.log(`Standalone child folder: ${childDir} (no worktree)`);
214
+ } else {
215
+ const projectRoot2 = opts.directory || lead.directory;
216
+ if (!projectRoot2) {
217
+ console.error("Could not determine the lead project directory.");
218
+ process.exit(1);
219
+ }
220
+ base = opts.baseBranch || currentBranch(projectRoot2) || "main";
221
+ try {
222
+ wt = createWorktree(projectRoot2);
223
+ } catch (e) {
224
+ console.error(`Failed to create worktree: ${e.message}`);
225
+ process.exit(1);
226
+ }
227
+ childDir = wt.path;
228
+ console.log(`Worktree: ${wt.branch} \u2192 ${wt.path}`);
181
229
  }
182
- console.log(`Worktree: ${wt.branch} \u2192 ${wt.path}`);
230
+ const projectRoot = lead.directory || opts.directory || childDir;
183
231
  const result = await machine.spawnSession({
184
- directory: wt.path,
232
+ directory: childDir,
185
233
  agent: "claude",
186
234
  parentSessionId: lead.sessionId,
187
235
  permissionMode: "bypassPermissions"
@@ -192,14 +240,15 @@ async function featureStart(brief, opts = {}) {
192
240
  }
193
241
  const childId = result.sessionId;
194
242
  try {
243
+ const delegatedWhere = standalone ? `standalone folder \`${childDir}\`` : `\`${wt.branch}\` (base \`${base}\`)`;
195
244
  let linkedId = opts.issueId;
196
245
  if (linkedId) {
197
- const u = updateIssue(projectRoot, linkedId, { disposition: "crew", session: childId, branch: wt.branch, status: "in_progress" });
246
+ const u = updateIssue(projectRoot, linkedId, { disposition: "crew", session: childId, owner: leadId, ...standalone ? {} : { branch: wt.branch }, status: "in_progress" });
198
247
  if (!u) {
199
248
  console.warn(`Note: issue ${linkedId} not found \u2014 creating a new crew issue instead.`);
200
249
  linkedId = void 0;
201
250
  } else {
202
- addComment(projectRoot, u.id, `Delegated to crew child ${childId.slice(0, 8)} on \`${wt.branch}\` (base \`${base}\`).`);
251
+ addComment(projectRoot, u.id, `Delegated to crew child ${childId.slice(0, 8)} on ${delegatedWhere}.`);
203
252
  linkedId = u.id;
204
253
  }
205
254
  }
@@ -210,11 +259,13 @@ async function featureStart(brief, opts = {}) {
210
259
  disposition: "crew",
211
260
  triaged: true,
212
261
  session: childId,
262
+ owner: leadId,
213
263
  scope: "project",
264
+ // owner=lead (#0099): visible to the lead, not unrelated sessions
214
265
  ...opts.oracle?.trim() ? { verify: { type: "command", text: opts.oracle.trim() } } : {},
215
266
  body: brief.trim()
216
267
  });
217
- updateIssue(projectRoot, created.id, { branch: wt.branch });
268
+ if (!standalone) updateIssue(projectRoot, created.id, { branch: wt.branch });
218
269
  linkedId = created.id;
219
270
  }
220
271
  console.log(` issue: #${linkedId} (crew, child ${childId.slice(0, 8)})`);
@@ -229,7 +280,7 @@ async function featureStart(brief, opts = {}) {
229
280
  }
230
281
  const oracle = opts.oracle?.trim();
231
282
  const patch = {
232
- crew: { role: "feature", feature: brief.trim(), branch: wt.branch, worktreePath: wt.path, baseBranch: base }
283
+ crew: standalone ? standaloneCrewMeta(brief.trim(), childDir) : worktreeCrewMeta(brief.trim(), wt.branch, wt.path, base)
233
284
  };
234
285
  let judges = [];
235
286
  if (oracle) {
@@ -250,12 +301,7 @@ async function featureStart(brief, opts = {}) {
250
301
  await rpc(machine, childId, "sendMessage", {
251
302
  content: JSON.stringify({
252
303
  role: "user",
253
- content: { type: "text", text: `You are a crew feature child on branch \`${wt.branch}\` (base \`${base}\`), reporting to lead \`${lead.sessionId.slice(0, 8)}\`.
254
-
255
- Feature brief:
256
- ${brief.trim()}
257
-
258
- Work in this worktree. Keep tests green, report milestones with \`svamp feature report\`, and when done run \`svamp feature done\` so your lead can review & merge. After merge you'll be closed.` },
304
+ content: { type: "text", text: standalone ? standaloneBrief(childDir, lead.sessionId.slice(0, 8), brief.trim()) : worktreeBrief(wt.branch, base, lead.sessionId.slice(0, 8), brief.trim()) },
259
305
  meta: { sentFrom: "svamp-cli", crew: "feature-brief" }
260
306
  })
261
307
  });
@@ -264,8 +310,12 @@ Work in this worktree. Keep tests green, report milestones with \`svamp feature
264
310
  }
265
311
  console.log(`Feature child started: ${childId}`);
266
312
  console.log(` brief: ${brief.trim().slice(0, 80)}${brief.trim().length > 80 ? "\u2026" : ""}`);
267
- console.log(` branch: ${wt.branch} (merges back to ${base})`);
268
- console.log(judges.length ? ` gate: ${judges.map((j) => j.type).join(" \u2192 ")} (oracle auto-approves \u2192 lead merges)` : ` gate: none \u2014 child runs \`feature done\` when complete; lead reviews & merges`);
313
+ if (standalone) {
314
+ console.log(` folder: ${childDir} (standalone \u2014 no branch; done detaches & closes)`);
315
+ } else {
316
+ console.log(` branch: ${wt.branch} (merges back to ${base})`);
317
+ }
318
+ console.log(judges.length ? ` gate: ${judges.map((j) => j.type).join(" \u2192 ")} (oracle auto-approves \u2192 lead ${standalone ? "closes" : "merges"})` : ` gate: none \u2014 child runs \`feature done\` when complete; lead ${standalone ? "detaches & closes" : "reviews & merges"}`);
269
319
  } finally {
270
320
  await server.disconnect();
271
321
  }
@@ -279,25 +329,32 @@ async function featureList(opts = {}) {
279
329
  const children = sessions.filter((s) => s.metadata?.parentSessionId === lead.sessionId);
280
330
  const rows = children.map((c) => {
281
331
  const crew = c.metadata?.crew || {};
282
- const dir = c.directory || crew.worktreePath || "";
332
+ const standalone = isStandaloneChild(crew);
333
+ const dir = childDirectory(crew, c.directory);
283
334
  const base = crew.baseBranch || "main";
284
- const branch = crew.branch || (dir && existsSync(dir) ? currentBranch(dir) : "") || "";
285
- let ahead = 0, behind = 0, dirty = false, gone = false;
286
- if (dir && existsSync(dir)) {
287
- const ab = aheadBehind(dir, base);
288
- ahead = ab.ahead;
289
- behind = ab.behind;
290
- try {
291
- dirty = worktreeStatus(dir) !== "";
292
- } catch {
335
+ let branch = "", ahead = 0, behind = 0, dirty = false, gone = false;
336
+ if (standalone) {
337
+ gone = !(dir && existsSync(dir));
338
+ } else {
339
+ branch = crew.branch || (dir && existsSync(dir) ? currentBranch(dir) : "") || "";
340
+ if (dir && existsSync(dir)) {
341
+ const ab = aheadBehind(dir, base);
342
+ ahead = ab.ahead;
343
+ behind = ab.behind;
344
+ try {
345
+ dirty = worktreeStatus(dir) !== "";
346
+ } catch {
347
+ }
348
+ } else if (crew.worktreePath) {
349
+ gone = true;
293
350
  }
294
- } else if (crew.worktreePath) {
295
- gone = true;
296
351
  }
297
352
  return {
298
353
  id: c.sessionId,
299
354
  title: c.metadata?.summary?.text || c.metadata?.name || "",
300
355
  role: crew.role || (crew.worktreePath ? "feature" : ""),
356
+ standalone,
357
+ dir,
301
358
  branch,
302
359
  base,
303
360
  ahead,
@@ -319,10 +376,11 @@ async function featureList(opts = {}) {
319
376
  console.log(`Features under lead ${lead.sessionId.slice(0, 8)}:
320
377
  `);
321
378
  for (const r of rows) {
322
- const state = r.gone ? "worktree-gone" : r.active ? "active" : "idle";
323
- const ab = r.gone ? "" : `+${r.ahead}/-${r.behind}${r.dirty ? " \u2717dirty" : ""}`;
379
+ const state = r.standalone ? r.gone ? "folder-gone" : r.active ? "standalone" : "standalone\xB7idle" : r.gone ? "worktree-gone" : r.active ? "active" : "idle";
380
+ const ab = r.standalone || r.gone ? "" : `+${r.ahead}/-${r.behind}${r.dirty ? " \u2717dirty" : ""}`;
381
+ const ref = r.standalone ? r.dir || "\u2014" : r.branch || "\u2014";
324
382
  const title = (r.feature || r.title || "").slice(0, 48);
325
- console.log(` ${r.id.slice(0, 8)} ${state.padEnd(13)} ${(r.branch || "\u2014").padEnd(28)} ${ab.padEnd(14)} ${title}`);
383
+ console.log(` ${r.id.slice(0, 8)} ${state.padEnd(15)} ${ref.padEnd(28)} ${ab.padEnd(14)} ${title}`);
326
384
  }
327
385
  console.log(`
328
386
  Review a finished feature with: svamp feature merge <id>`);
@@ -364,6 +422,19 @@ async function featureDone(opts = {}) {
364
422
  process.exit(1);
365
423
  }
366
424
  const crew = meta?.crew || {};
425
+ if (isStandaloneChild(crew)) {
426
+ const dir2 = childDirectory(crew, meta?.path);
427
+ const body2 = [
428
+ `<done-request child="${selfId}" standalone="true" folder="${dir2}">`,
429
+ crew.feature ? `Feature: ${crew.feature}` : "",
430
+ opts.summary ? `Summary: ${opts.summary.trim()}` : "",
431
+ `No branch to merge (standalone folder). Detach & close with: svamp feature merge ${selfId.slice(0, 8)}`,
432
+ `</done-request>`
433
+ ].filter(Boolean).join("\n");
434
+ await inbox(machine, selfId, parent, body2, `done-request (standalone)`);
435
+ console.log(`Sent done-request to lead ${parent.slice(0, 8)} (standalone folder ${dir2 || "?"}; no branch to merge).`);
436
+ return;
437
+ }
367
438
  const dir = meta?.path || crew.worktreePath || "";
368
439
  const base = crew.baseBranch || "main";
369
440
  const branch = crew.branch || (dir && existsSync(dir) ? currentBranch(dir) : "") || "";
@@ -394,6 +465,18 @@ async function featureMerge(childPartial, opts = {}) {
394
465
  try {
395
466
  const meta = await getMeta(machine, childId);
396
467
  const crew = meta?.crew || {};
468
+ if (isStandaloneChild(crew)) {
469
+ const dir = childDirectory(crew, meta?.path);
470
+ if (!opts.keepChild) {
471
+ try {
472
+ await machine.archiveSession(childId);
473
+ } catch (e) {
474
+ console.warn(`Could not archive standalone child: ${e.message}`);
475
+ }
476
+ }
477
+ console.log(`\u2705 Detached standalone child ${childId.slice(0, 8)} (folder ${dir || "?"}; no branch to merge) \u2014 ${opts.keepChild ? "kept" : "archived"}.`);
478
+ return;
479
+ }
397
480
  const worktreePath = crew.worktreePath || meta?.path || "";
398
481
  if (!worktreePath) {
399
482
  console.error("Could not determine the child worktree path.");
@@ -482,14 +565,18 @@ function positionals(args, valueFlags) {
482
565
  const FEATURE_HELP = `Usage: svamp feature <command>
483
566
 
484
567
  start "<brief>" [--oracle "<cmd>"] [--base <branch>] [--action nudge|block]
485
- [--max N] [--dir <path>] [--lead <id>] [-m <machine>]
486
- Spawn a managed worktree child of the lead + attach a loop gate (oracle\u2192parent).
568
+ [--max N] [--dir <path>] [--no-worktree] [--lead <id>] [-m <machine>]
569
+ Spawn a managed child of the lead + attach a loop gate (oracle\u2192parent).
570
+ Default: a git worktree + branch off the lead's project root (merges back).
571
+ --no-worktree --dir <folder>: run the child directly IN <folder> (a subdir or
572
+ sibling project) with NO worktree/branch \u2014 done/merge just detach & close it.
487
573
  list [--json] [--lead <id>] [-m <machine>]
488
- The lead's feature children: branch, ahead/behind, status.
574
+ The lead's feature children: branch/folder, ahead/behind, status.
489
575
  report "<text>" [--blocker] [-m <machine>] (run by a child) progress/blocker \u2192 lead
490
- done [--summary "<text>"] [-m <machine>] (run by a child) send a merge-request \u2192 lead
576
+ done [--summary "<text>"] [-m <machine>] (run by a child) merge-request (worktree) or done-request (standalone) \u2192 lead
491
577
  merge <child-id> [--keep-child] [--no-delete-branch] [-m <machine>]
492
- (run by the lead) verify \u2192 merge \u2192 remove worktree \u2192 archive child.`;
578
+ (run by the lead) worktree: verify \u2192 merge \u2192 remove worktree \u2192 archive child.
579
+ standalone: just detach & archive the child (no branch to merge).`;
493
580
  async function crewCommand(args) {
494
581
  const sub = args[0];
495
582
  const rest = args.slice(1);
@@ -508,6 +595,7 @@ async function crewCommand(args) {
508
595
  leadId: lead,
509
596
  machineId,
510
597
  directory: flag(rest, "--dir", "-d"),
598
+ noWorktree: has(rest, "--no-worktree", "--standalone"),
511
599
  oracle: flag(rest, "--oracle"),
512
600
  baseBranch: flag(rest, "--base"),
513
601
  issueId: flag(rest, "--issue"),
@@ -1,7 +1,7 @@
1
1
  import os from 'os';
2
2
  import fs__default from 'fs';
3
3
  import { resolve, join, relative } from 'path';
4
- import { C as parseFrontmatter, D as getSkillsServer, E as getSkillsWorkspaceName, F as getSkillsCollectionName, G as fetchWithTimeout, H as searchSkills, I as SKILLS_DIR, J as getSkillInfo, K as downloadSkillFile, L as listSkillFiles } from './run-Dn-FEr9q.mjs';
4
+ import { D as parseFrontmatter, E as getSkillsServer, F as getSkillsWorkspaceName, G as getSkillsCollectionName, H as fetchWithTimeout, I as searchSkills, J as SKILLS_DIR, K as getSkillInfo, L as downloadSkillFile, M as listSkillFiles } from './run-Cxq7C5mA.mjs';
5
5
  import 'fs/promises';
6
6
  import 'url';
7
7
  import 'child_process';
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import os from 'node:os';
4
- import { c as connectToHypha } from './run-Dn-FEr9q.mjs';
4
+ import { c as connectToHypha } from './run-Cxq7C5mA.mjs';
5
5
  import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-HydRNEt7.mjs';
6
6
  import 'os';
7
7
  import 'fs/promises';
@@ -4,7 +4,7 @@ import { mkdirSync, writeFileSync, unlinkSync, existsSync, chmodSync, readFileSy
4
4
  import { join } from 'path';
5
5
  import { homedir, platform, arch } from 'os';
6
6
  import { randomUUID, createHash } from 'crypto';
7
- import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-Dn-FEr9q.mjs';
7
+ import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-Cxq7C5mA.mjs';
8
8
  import 'fs/promises';
9
9
  import 'url';
10
10
  import 'node:crypto';
@@ -1,5 +1,5 @@
1
- import { M as resolveModel, _ as describeMisconfiguration, $ as buildMachineDeps } from './run-Dn-FEr9q.mjs';
2
- import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-CjLQLnk1.mjs';
1
+ import { N as resolveModel, $ as describeMisconfiguration, a0 as buildMachineDeps } from './run-Cxq7C5mA.mjs';
2
+ import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-p3YKDEQ0.mjs';
3
3
  import { WebSocket } from 'ws';
4
4
  import { execSync, spawn } from 'child_process';
5
5
  import 'os';
@@ -160,6 +160,44 @@ data: ${JSON.stringify({ error: out.error })}
160
160
  });
161
161
  return;
162
162
  }
163
+ m = u.pathname.match(/^\/channel\/([\w.-]+)\/upload$/);
164
+ if (m) {
165
+ const cid = m[1];
166
+ const rpc2 = await findOwner(deps, cid);
167
+ if (!rpc2?.channelUpload) {
168
+ json(404, { error: "channel not found" });
169
+ return;
170
+ }
171
+ let chunks2 = "";
172
+ req.on("data", (d) => {
173
+ chunks2 += d;
174
+ if (chunks2.length > 40 * 1024 * 1024) req.destroy();
175
+ });
176
+ req.on("end", async () => {
177
+ let body = {};
178
+ try {
179
+ body = chunks2 ? JSON.parse(chunks2) : {};
180
+ } catch {
181
+ json(400, { error: "invalid JSON body" });
182
+ return;
183
+ }
184
+ try {
185
+ const out = await rpc2.channelUpload({
186
+ channel: cid,
187
+ key: keyOf(),
188
+ from: body.from,
189
+ session: body.session,
190
+ filename: body.filename,
191
+ content_base64: body.content_base64 ?? body.contentBase64,
192
+ content_type: body.content_type ?? body.contentType
193
+ });
194
+ json(out?.error ? out.error === "channel not found" ? 404 : 400 : 200, out);
195
+ } catch (e) {
196
+ json(500, { error: e?.message || String(e) });
197
+ }
198
+ });
199
+ return;
200
+ }
163
201
  m = u.pathname.match(/^\/channel\/([\w.-]+)\/tool$/);
164
202
  if (m) {
165
203
  const cid = m[1];
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-Dn-FEr9q.mjs';
1
+ export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-Cxq7C5mA.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -1,5 +1,5 @@
1
1
  var name = "svamp-cli";
2
- var version = "0.2.194";
2
+ var version = "0.2.196";
3
3
  var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
4
4
  var author = "Amun AI AB";
5
5
  var license = "SEE LICENSE IN LICENSE";
@@ -19,7 +19,7 @@ var exports$1 = {
19
19
  var scripts = {
20
20
  build: "rm -rf dist bin/skills && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/loop bin/skills/loop && cp -r ../../skills/crew bin/skills/crew && tsc --noEmit && pkgroll",
21
21
  typecheck: "tsc --noEmit",
22
- test: "npx tsx test/test-context-window.mjs && npx tsx test/test-ratelimit-retry.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-sharing-notify-sync.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-loop-activation.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-btw-proxy-env.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-inbox-cross-machine.mjs && npx tsx test/test-issue-store.mjs && npx tsx test/test-issue-close-gate.mjs && npx tsx test/test-issue-rpc.mjs && npx tsx test/test-workflow-store.mjs && npx tsx test/test-workflow-rpc.mjs && npx tsx test/test-workflow-scheduler.mjs && npx tsx test/test-serve-link-subdomain.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && npx tsx test/test-frpc-status.mjs && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-shared-session-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs && npx tsx test/test-crew-verdict-routing.mjs",
22
+ test: "npx tsx test/test-context-window.mjs && npx tsx test/test-ratelimit-retry.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-sharing-notify-sync.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-loop-activation.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-btw-proxy-env.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-inbox-cross-machine.mjs && npx tsx test/test-issue-store.mjs && npx tsx test/test-issue-close-gate.mjs && npx tsx test/test-issue-rpc.mjs && npx tsx test/test-workflow-store.mjs && npx tsx test/test-workflow-rpc.mjs && npx tsx test/test-workflow-scheduler.mjs && npx tsx test/test-serve-link-subdomain.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && npx tsx test/test-frpc-status.mjs && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-shared-session-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-channel-upload.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs && npx tsx test/test-crew-verdict-routing.mjs && npx tsx test/test-crew-standalone.mjs",
23
23
  "test:hypha": "node --no-warnings test/test-hypha-service.mjs",
24
24
  dev: "tsx src/cli.ts",
25
25
  "dev:daemon": "tsx src/cli.ts daemon start-sync",
@@ -1,5 +1,5 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- import { m as resolveProjectRoot } from './run-Dn-FEr9q.mjs';
2
+ import { m as resolveProjectRoot } from './run-Cxq7C5mA.mjs';
3
3
  import { g as getWorkflow, w as workflowSteps, s as setWorkflowEnabled, r as removeWorkflow, a as saveWorkflow, b as rawWorkflow, l as listWorkflows } from './store-DEZ8e-uE.mjs';
4
4
  import 'os';
5
5
  import 'fs/promises';
@@ -1,4 +1,4 @@
1
- import { m as resolveProjectRoot, u as updateIssue, n as getIssue, o as addComment, p as addIssue, q as listIssues, t as searchIssues } from './run-Dn-FEr9q.mjs';
1
+ import { m as resolveProjectRoot, u as updateIssue, n as getIssue, o as addComment, p as addIssue, q as listIssues, t as searchIssues, v as isVisibleTo } from './run-Cxq7C5mA.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -50,7 +50,7 @@ function buildVerify(p) {
50
50
  }
51
51
  function ownerScoped(items, session) {
52
52
  if (!session) return items;
53
- return items.filter((i) => i.scope === "project" || i.session === session);
53
+ return items.filter((i) => isVisibleTo(i, session));
54
54
  }
55
55
  const STATUS_MAP = {
56
56
  ready: "ready",
@@ -101,6 +101,10 @@ function issueRpc(cwd, params, deps = {}) {
101
101
  labels: Array.isArray(params.labels) ? params.labels.map(String) : [],
102
102
  verify: buildVerify(params) ?? null,
103
103
  session: scope === "session" ? owner : null,
104
+ // Record the creating session as the owner (#0099) so a project issue stays
105
+ // visible to its creator/parent (and the "all" view) but doesn't leak into
106
+ // unrelated parallel sessions. Null owner (no session) → globally shared.
107
+ owner,
104
108
  original,
105
109
  body
106
110
  });
@@ -1,4 +1,4 @@
1
- import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { a0 as applyClaudeProxyEnv, a1 as composeSessionId, a2 as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, a3 as generateHookSettings } from './run-Dn-FEr9q.mjs';
1
+ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { a1 as applyClaudeProxyEnv, a2 as composeSessionId, a3 as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, a4 as generateHookSettings } from './run-Cxq7C5mA.mjs';
2
2
  import os from 'node:os';
3
3
  import { resolve, join } from 'node:path';
4
4
  import { existsSync, readFileSync, watch } from 'node:fs';