@craftspace/cli 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -19258,6 +19258,12 @@ var CreateConnectionBodySchema = external_exports.object({
19258
19258
  // sent it, and can start a connect for that one kit and nothing else.
19259
19259
  inviteToken: external_exports.string().optional()
19260
19260
  });
19261
+ var ReplaceConnectionSecretBodySchema = external_exports.object({
19262
+ mcpUrl: external_exports.url().optional(),
19263
+ authToken: external_exports.string().min(1).optional(),
19264
+ dsn: external_exports.string().min(1).optional(),
19265
+ apiKey: external_exports.string().min(1).optional()
19266
+ });
19261
19267
  var ConnectionIdParamsSchema = external_exports.object({ id: external_exports.string() });
19262
19268
  var PendingOAuthSchema = external_exports.object({
19263
19269
  id: external_exports.string(),
@@ -20221,7 +20227,7 @@ var MemberActivityListResponse = external_exports.object({
20221
20227
  // dist/machine.js
20222
20228
  import { execFile as execFile3 } from "node:child_process";
20223
20229
  import { mkdir, readFile as readFile2, readdir, rm, writeFile } from "node:fs/promises";
20224
- import os3 from "node:os";
20230
+ import os4 from "node:os";
20225
20231
  import path4 from "node:path";
20226
20232
  import { promisify as promisify3 } from "node:util";
20227
20233
 
@@ -22091,6 +22097,7 @@ var dist_default6 = createPrompt((config2, done) => {
22091
22097
 
22092
22098
  // dist/run.js
22093
22099
  import { spawn } from "node:child_process";
22100
+ import os from "node:os";
22094
22101
  var runner = {
22095
22102
  attended(argv) {
22096
22103
  return new Promise((resolve) => {
@@ -22101,6 +22108,7 @@ var runner = {
22101
22108
  },
22102
22109
  async unattended({ work, onProgress }) {
22103
22110
  const argv = headlessArgv(work);
22111
+ const raw = argv[0] === "script";
22104
22112
  let output = "";
22105
22113
  let agentSessionId = work.agentSessionId ?? void 0;
22106
22114
  let lastText = "";
@@ -22126,10 +22134,24 @@ var runner = {
22126
22134
  }
22127
22135
  };
22128
22136
  const code = await new Promise((resolve) => {
22129
- const child = spawn(argv[0], argv.slice(1), { stdio: ["ignore", "pipe", "pipe"] });
22137
+ const child = spawn(argv[0], argv.slice(1), {
22138
+ stdio: ["ignore", "pipe", "pipe"],
22139
+ cwd: os.homedir(),
22140
+ env: {
22141
+ ...process.env,
22142
+ TERM: process.env.TERM ?? "xterm-256color",
22143
+ COLUMNS: process.env.COLUMNS ?? "120",
22144
+ LINES: process.env.LINES ?? "40"
22145
+ }
22146
+ });
22147
+ append("");
22130
22148
  child.stdout.setEncoding("utf8");
22131
22149
  child.stderr.setEncoding("utf8");
22132
22150
  child.stdout.on("data", (chunk) => {
22151
+ if (raw) {
22152
+ append(chunk);
22153
+ return;
22154
+ }
22133
22155
  pending += chunk;
22134
22156
  const lines = pending.split("\n");
22135
22157
  pending = lines.pop() ?? "";
@@ -22179,8 +22201,11 @@ function headlessArgv(work) {
22179
22201
  return [argv[0], "-p", argv.slice(1).join(" "), ...streaming];
22180
22202
  }
22181
22203
  function underTty(argv) {
22182
- return process.platform === "linux" ? ["script", "-qec", argv.join(" "), "/dev/null"] : ["script", "-q", "/dev/null", ...argv];
22204
+ const line = `stty rows ${PTY_ROWS} cols ${PTY_COLS} 2>/dev/null; exec ${argv.join(" ")}`;
22205
+ return process.platform === "linux" ? ["script", "-qec", line, "/dev/null"] : ["script", "-q", "/dev/null", "sh", "-c", line];
22183
22206
  }
22207
+ var PTY_ROWS = 40;
22208
+ var PTY_COLS = 120;
22184
22209
  function isAgent(command) {
22185
22210
  return AGENTS.has((command ?? "").split("/").pop() ?? "");
22186
22211
  }
@@ -22209,7 +22234,7 @@ function parseJson(line) {
22209
22234
 
22210
22235
  // dist/setup.js
22211
22236
  import { execFile as execFile2 } from "node:child_process";
22212
- import os2 from "node:os";
22237
+ import os3 from "node:os";
22213
22238
  import path3 from "node:path";
22214
22239
  import { promisify as promisify2 } from "node:util";
22215
22240
 
@@ -22217,12 +22242,12 @@ import { promisify as promisify2 } from "node:util";
22217
22242
  import { execFile } from "node:child_process";
22218
22243
  import net from "node:net";
22219
22244
  import { access, readFile } from "node:fs/promises";
22220
- import os from "node:os";
22245
+ import os2 from "node:os";
22221
22246
  import path2 from "node:path";
22222
22247
  import { promisify } from "node:util";
22223
22248
  var run = promisify(execFile);
22224
22249
  var probe = {
22225
- async tools({ mcpPath = path2.join(os.homedir(), ".mcp.json") } = {}) {
22250
+ async tools({ mcpPath = path2.join(os2.homedir(), ".mcp.json") } = {}) {
22226
22251
  const [brain, claude, codex, git, chrome, ssh] = await Promise.all([
22227
22252
  brainWired(mcpPath),
22228
22253
  version2({ id: "claude", command: "claude" }),
@@ -22268,7 +22293,7 @@ async function gitAndGithub() {
22268
22293
  };
22269
22294
  }
22270
22295
  async function githubAccount() {
22271
- const raw = await readFile(path2.join(os.homedir(), ".config", "gh", "hosts.yml"), "utf8").catch(() => null);
22296
+ const raw = await readFile(path2.join(os2.homedir(), ".config", "gh", "hosts.yml"), "utf8").catch(() => null);
22272
22297
  return raw === null ? null : githubUser(raw);
22273
22298
  }
22274
22299
  function githubUser(hostsYml) {
@@ -22308,7 +22333,7 @@ async function firstLine(command, args) {
22308
22333
  return line === "" ? { ran: false, why: "broken" } : { ran: true, line: line.slice(0, 200) };
22309
22334
  }
22310
22335
  function short(target) {
22311
- return target.startsWith(os.homedir()) ? `~${target.slice(os.homedir().length)}` : target;
22336
+ return target.startsWith(os2.homedir()) ? `~${target.slice(os2.homedir().length)}` : target;
22312
22337
  }
22313
22338
  var MAC_CHROME = ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"];
22314
22339
  var LINUX_CHROME = ["google-chrome", "google-chrome-stable", "chromium"];
@@ -22363,8 +22388,8 @@ function toolDirs() {
22363
22388
  return [
22364
22389
  path3.dirname(process.execPath),
22365
22390
  ...prefix === void 0 ? [] : [path3.join(prefix, "bin")],
22366
- path3.join(os2.homedir(), ".local", "bin"),
22367
- path3.join(os2.homedir(), ".npm-global", "bin"),
22391
+ path3.join(os3.homedir(), ".local", "bin"),
22392
+ path3.join(os3.homedir(), ".npm-global", "bin"),
22368
22393
  "/opt/homebrew/bin",
22369
22394
  "/usr/local/bin"
22370
22395
  ];
@@ -22419,7 +22444,7 @@ var RETRY_AFTER_MS = 216e5;
22419
22444
  var run3 = promisify3(execFile3);
22420
22445
  var machine = {
22421
22446
  home() {
22422
- return process.env.CRAFTSPACE_CLI_HOME ?? path4.join(os3.homedir(), ".craftspace");
22447
+ return process.env.CRAFTSPACE_CLI_HOME ?? path4.join(os4.homedir(), ".craftspace");
22423
22448
  },
22424
22449
  async login({ token, url: url2, install }) {
22425
22450
  const answer = await call({
@@ -22428,11 +22453,11 @@ var machine = {
22428
22453
  method: "POST",
22429
22454
  path: "/api/machines/login",
22430
22455
  body: {
22431
- name: os3.hostname().split(".")[0] ?? "machine",
22456
+ name: os4.hostname().split(".")[0] ?? "machine",
22432
22457
  platform: `${process.platform}-${process.arch}`,
22433
- sshUser: os3.userInfo().username,
22434
- cores: os3.cpus().length,
22435
- memoryGb: Math.max(1, Math.round(os3.totalmem() / 1024 ** 3)),
22458
+ sshUser: os4.userInfo().username,
22459
+ cores: os4.cpus().length,
22460
+ memoryGb: Math.max(1, Math.round(os4.totalmem() / 1024 ** 3)),
22436
22461
  cli: CLI_VERSION,
22437
22462
  publicKey: await ensureKey()
22438
22463
  },
@@ -22495,7 +22520,7 @@ Signed in as ${signed.ownerName}. This machine is ${signed.name}.
22495
22520
  sessions: await readSessions(),
22496
22521
  runs: drainReports(),
22497
22522
  tools: await tools(),
22498
- sshUser: os3.userInfo().username
22523
+ sshUser: os4.userInfo().username
22499
22524
  },
22500
22525
  schema: MachineBeatResponseSchema
22501
22526
  });
@@ -22736,7 +22761,7 @@ async function ensureKey() {
22736
22761
  if (existing !== null)
22737
22762
  return existing.trim();
22738
22763
  await mkdir(machine.home(), { recursive: true, mode: 448 });
22739
- await run3("ssh-keygen", ["-t", "ed25519", "-N", "", "-q", "-f", priv, "-C", `craftspace-${os3.hostname()}`]);
22764
+ await run3("ssh-keygen", ["-t", "ed25519", "-N", "", "-q", "-f", priv, "-C", `craftspace-${os4.hostname()}`]);
22740
22765
  return (await readFile2(pub, "utf8")).trim();
22741
22766
  }
22742
22767
  async function readConfig() {
@@ -22764,7 +22789,7 @@ async function readSessions() {
22764
22789
  return sessions.slice(0, MAX_REPORTED_SESSIONS);
22765
22790
  }
22766
22791
  async function writeAuthorizedKeys(keys) {
22767
- const target = path4.join(os3.homedir(), ".ssh", "authorized_keys");
22792
+ const target = path4.join(os4.homedir(), ".ssh", "authorized_keys");
22768
22793
  const current = await readFile2(target, "utf8").catch(() => "");
22769
22794
  const before = current.split(KEYS_BEGIN)[0] ?? "";
22770
22795
  const after = current.includes(KEYS_END) ? current.split(KEYS_END)[1] ?? "" : "";
@@ -22804,6 +22829,31 @@ async function writeAgentConfigs({ url: url2, token, writes }) {
22804
22829
  owned.push(target);
22805
22830
  }
22806
22831
  await writeFile(ownedPath(), JSON.stringify({ paths: owned }, null, 2));
22832
+ await acceptClaudeGates(serversIn(planned));
22833
+ }
22834
+ function serversIn(writes) {
22835
+ return writes.flatMap((write) => isPlainObject3(write.contents.mcpServers) ? Object.keys(write.contents.mcpServers) : []);
22836
+ }
22837
+ async function acceptClaudeGates(servers) {
22838
+ const target = path4.join(os4.homedir(), ".claude.json");
22839
+ const raw = await readFile2(target, "utf8").catch(() => null);
22840
+ if (raw === null)
22841
+ return;
22842
+ const parsed = JSON.parse(raw);
22843
+ const projects = isPlainObject3(parsed.projects) ? parsed.projects : {};
22844
+ const found = projects[os4.homedir()];
22845
+ const here = isPlainObject3(found) ? { ...found } : {};
22846
+ const enabled = /* @__PURE__ */ new Set([...Array.isArray(here.enabledMcpjsonServers) ? here.enabledMcpjsonServers : [], ...servers]);
22847
+ if (here.hasTrustDialogAccepted === true && enabled.size === asArray(here.enabledMcpjsonServers).length)
22848
+ return;
22849
+ here.hasTrustDialogAccepted = true;
22850
+ here.enabledMcpjsonServers = [...enabled];
22851
+ const next = { ...parsed, projects: { ...projects, [os4.homedir()]: here } };
22852
+ await writeFile(target, `${JSON.stringify(next, null, 2)}
22853
+ `, { mode: 384 });
22854
+ }
22855
+ function asArray(value) {
22856
+ return Array.isArray(value) ? value : [];
22807
22857
  }
22808
22858
  function mergeInto(current, incoming) {
22809
22859
  const merged = { ...current };
@@ -22851,7 +22901,7 @@ async function installService() {
22851
22901
  return;
22852
22902
  }
22853
22903
  if (process.platform === "linux") {
22854
- const dir = path4.join(os3.homedir(), ".config", "systemd", "user");
22904
+ const dir = path4.join(os4.homedir(), ".config", "systemd", "user");
22855
22905
  await mkdir(dir, { recursive: true });
22856
22906
  await writeFile(path4.join(dir, `${SERVICE_NAME}.service`), systemdUnit());
22857
22907
  await run3("systemctl", ["--user", "daemon-reload"]).catch(() => void 0);
@@ -22859,7 +22909,7 @@ async function installService() {
22859
22909
  return;
22860
22910
  }
22861
22911
  if (process.platform === "darwin") {
22862
- const target = path4.join(os3.homedir(), "Library", "LaunchAgents", `${LAUNCH_LABEL}.plist`);
22912
+ const target = path4.join(os4.homedir(), "Library", "LaunchAgents", `${LAUNCH_LABEL}.plist`);
22863
22913
  await mkdir(path4.dirname(target), { recursive: true });
22864
22914
  await writeFile(target, launchdPlist());
22865
22915
  await run3("launchctl", ["unload", target]).catch(() => void 0);
@@ -22869,11 +22919,11 @@ async function installService() {
22869
22919
  async function uninstallService() {
22870
22920
  if (process.platform === "linux") {
22871
22921
  await run3("systemctl", ["--user", "disable", "--now", SERVICE_NAME]).catch(() => void 0);
22872
- await rm(path4.join(os3.homedir(), ".config", "systemd", "user", `${SERVICE_NAME}.service`), { force: true });
22922
+ await rm(path4.join(os4.homedir(), ".config", "systemd", "user", `${SERVICE_NAME}.service`), { force: true });
22873
22923
  return;
22874
22924
  }
22875
22925
  if (process.platform === "darwin") {
22876
- const target = path4.join(os3.homedir(), "Library", "LaunchAgents", `${LAUNCH_LABEL}.plist`);
22926
+ const target = path4.join(os4.homedir(), "Library", "LaunchAgents", `${LAUNCH_LABEL}.plist`);
22877
22927
  await run3("launchctl", ["unload", target]).catch(() => void 0);
22878
22928
  await rm(target, { force: true });
22879
22929
  }
@@ -22911,7 +22961,7 @@ function entryPath() {
22911
22961
  return path4.join(path4.dirname(new URL(import.meta.url).pathname), "index.js");
22912
22962
  }
22913
22963
  function expand(target) {
22914
- return target.startsWith("~/") ? path4.join(os3.homedir(), target.slice(2)) : target;
22964
+ return target.startsWith("~/") ? path4.join(os4.homedir(), target.slice(2)) : target;
22915
22965
  }
22916
22966
  function configPath() {
22917
22967
  return path4.join(machine.home(), "config.json");
@@ -22923,9 +22973,9 @@ function ownedPath() {
22923
22973
  return path4.join(machine.home(), "owned.json");
22924
22974
  }
22925
22975
  function load2() {
22926
- const cores = Math.max(1, os3.cpus().length);
22927
- const busy = Math.min(100, (os3.loadavg()[0] ?? 0) / cores * 100);
22928
- const used = (os3.totalmem() - os3.freemem()) / os3.totalmem() * 100;
22976
+ const cores = Math.max(1, os4.cpus().length);
22977
+ const busy = Math.min(100, (os4.loadavg()[0] ?? 0) / cores * 100);
22978
+ const used = (os4.totalmem() - os4.freemem()) / os4.totalmem() * 100;
22929
22979
  return { cpuPercent: Math.round(busy), memoryPercent: Math.round(used) };
22930
22980
  }
22931
22981
  function nextDelayMs(failures, busy = false) {
package/dist/machine.js CHANGED
@@ -348,8 +348,6 @@ async function readSessions() {
348
348
  }
349
349
  return sessions.slice(0, MAX_REPORTED_SESSIONS);
350
350
  }
351
- // Only the block between our markers is ours. Everything a person put in this file by hand is left exactly
352
- // where it was, because the fastest way to lock a team out of its own box is to rewrite this file wholesale.
353
351
  async function writeAuthorizedKeys(keys) {
354
352
  const target = path.join(os.homedir(), '.ssh', 'authorized_keys');
355
353
  const current = await readFile(target, 'utf8').catch(() => '');
@@ -387,6 +385,30 @@ async function writeAgentConfigs({ url, token, writes, }) {
387
385
  owned.push(target);
388
386
  }
389
387
  await writeFile(ownedPath(), JSON.stringify({ paths: owned }, null, 2));
388
+ await acceptClaudeGates(serversIn(planned));
389
+ }
390
+ function serversIn(writes) {
391
+ return writes.flatMap((write) => isPlainObject(write.contents.mcpServers) ? Object.keys(write.contents.mcpServers) : []);
392
+ }
393
+ async function acceptClaudeGates(servers) {
394
+ const target = path.join(os.homedir(), '.claude.json');
395
+ const raw = await readFile(target, 'utf8').catch(() => null);
396
+ if (raw === null)
397
+ return;
398
+ const parsed = JSON.parse(raw);
399
+ const projects = isPlainObject(parsed.projects) ? parsed.projects : {};
400
+ const found = projects[os.homedir()];
401
+ const here = isPlainObject(found) ? { ...found } : {};
402
+ const enabled = new Set([...(Array.isArray(here.enabledMcpjsonServers) ? here.enabledMcpjsonServers : []), ...servers]);
403
+ if (here.hasTrustDialogAccepted === true && enabled.size === asArray(here.enabledMcpjsonServers).length)
404
+ return;
405
+ here.hasTrustDialogAccepted = true;
406
+ here.enabledMcpjsonServers = [...enabled];
407
+ const next = { ...parsed, projects: { ...projects, [os.homedir()]: here } };
408
+ await writeFile(target, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
409
+ }
410
+ function asArray(value) {
411
+ return Array.isArray(value) ? value : [];
390
412
  }
391
413
  function mergeInto(current, incoming) {
392
414
  const merged = { ...current };
package/dist/run.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { spawn } from 'node:child_process';
2
+ import os from 'node:os';
2
3
  import { MACHINE_RUN_OUTPUT_MAX, spreadIfDefined } from '@craftspace/shared';
3
4
  export const runner = {
4
5
  attended(argv) {
@@ -10,6 +11,7 @@ export const runner = {
10
11
  },
11
12
  async unattended({ work, onProgress, }) {
12
13
  const argv = headlessArgv(work);
14
+ const raw = argv[0] === 'script';
13
15
  let output = '';
14
16
  let agentSessionId = work.agentSessionId ?? undefined;
15
17
  let lastText = '';
@@ -33,10 +35,24 @@ export const runner = {
33
35
  }
34
36
  };
35
37
  const code = await new Promise((resolve) => {
36
- const child = spawn(argv[0], argv.slice(1), { stdio: ['ignore', 'pipe', 'pipe'] });
38
+ const child = spawn(argv[0], argv.slice(1), {
39
+ stdio: ['ignore', 'pipe', 'pipe'],
40
+ cwd: os.homedir(),
41
+ env: {
42
+ ...process.env,
43
+ TERM: process.env.TERM ?? 'xterm-256color',
44
+ COLUMNS: process.env.COLUMNS ?? '120',
45
+ LINES: process.env.LINES ?? '40',
46
+ },
47
+ });
48
+ append('');
37
49
  child.stdout.setEncoding('utf8');
38
50
  child.stderr.setEncoding('utf8');
39
51
  child.stdout.on('data', (chunk) => {
52
+ if (raw) {
53
+ append(chunk);
54
+ return;
55
+ }
40
56
  pending += chunk;
41
57
  const lines = pending.split('\n');
42
58
  pending = lines.pop() ?? '';
@@ -85,10 +101,13 @@ export function headlessArgv(work) {
85
101
  return [argv[0], '-p', argv.slice(1).join(' '), ...streaming];
86
102
  }
87
103
  function underTty(argv) {
104
+ const line = `stty rows ${PTY_ROWS} cols ${PTY_COLS} 2>/dev/null; exec ${argv.join(' ')}`;
88
105
  return process.platform === 'linux'
89
- ? ['script', '-qec', argv.join(' '), '/dev/null']
90
- : ['script', '-q', '/dev/null', ...argv];
106
+ ? ['script', '-qec', line, '/dev/null']
107
+ : ['script', '-q', '/dev/null', 'sh', '-c', line];
91
108
  }
109
+ const PTY_ROWS = 40;
110
+ const PTY_COLS = 120;
92
111
  function isAgent(command) {
93
112
  return AGENTS.has((command ?? '').split('/').pop() ?? '');
94
113
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@craftspace/cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "Sign a Mac or Linux machine into Craftspace and keep its agent setup in step.",
6
6
  "license": "MIT",