gipity 1.0.425 → 1.0.426

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.
@@ -209,8 +209,13 @@ export const addCommand = new Command('add')
209
209
  // The spinner is TTY-only, so piped/agent transcripts would otherwise show
210
210
  // a silent multi-second gap (favicon generation on a cold cache can take
211
211
  // ~10s, cli#117) - leave one stderr marker so the wait reads as work.
212
+ // Favicons are generated for templates only, so don't claim them on a kit
213
+ // install (kits are quick; the slow first-add path is template scaffolding).
212
214
  if (!opts.json && !process.stdout.isTTY) {
213
- console.error(muted('Installing (server writes files + generates favicons; first add for a title can take ~10s)...'));
215
+ const isKit = KITS.some(k => k.key === name);
216
+ console.error(muted(isKit
217
+ ? 'Installing kit (server-side install pipeline)...'
218
+ : 'Installing (server writes files + generates favicons; first add for a title can take ~10s)...'));
214
219
  }
215
220
  const doAdd = () => post(`/projects/${config.projectGuid}/add`, body);
216
221
  const res = opts.json
@@ -26,7 +26,10 @@ logsCommand
26
26
  for (const log of res.data) {
27
27
  const time = new Date(log.created_at).toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit' });
28
28
  const dur = log.duration_ms !== null ? `${log.duration_ms}ms`.padEnd(8) : ''.padEnd(8);
29
- const statusColor = log.status === 'success' ? success : log.status === 'error' ? clrError : warning;
29
+ // The runtime writes 'ok' | 'error' | 'limit_exceeded'; it has never
30
+ // written 'success'. Matching on that dead value painted every healthy
31
+ // invocation with the warning colour, so a clean log read as a problem.
32
+ const statusColor = log.status === 'ok' ? success : log.status === 'error' ? clrError : warning;
30
33
  const status = statusColor(log.status.padEnd(8));
31
34
  const trigger = muted(log.trigger_type.padEnd(8));
32
35
  const err = log.error_message ? ` ${clrError(`"${log.error_message}"`)}` : '';
@@ -150,6 +150,11 @@ function splitCsv(values) {
150
150
  function appendOption(value, previous = []) {
151
151
  return [...previous, value];
152
152
  }
153
+ // Pre-capture scripting lives on --action, but agents reach for the JS-intent
154
+ // names first and an "unknown option" alone doesn't name the right flag. Capture
155
+ // the common guesses as hidden decoys (taking a value, so they swallow the
156
+ // script) and redirect precisely — same pattern as `page eval`'s JS_DECOY_FLAGS.
157
+ const ACTION_DECOY_FLAGS = ['--eval', '--js', '--javascript', '--script', '--code', '--exec'];
153
158
  export const pageScreenshotCommand = new Command('screenshot')
154
159
  .description('Screenshot a web page')
155
160
  .argument('<url>', 'URL to screenshot')
@@ -172,6 +177,13 @@ export const pageScreenshotCommand = new Command('screenshot')
172
177
  // rather than reject it as an unknown option and send them on a --help detour.
173
178
  .addOption(new Option('--full-page', 'Alias for --full').hideHelp())
174
179
  .action((url, opts) => run('Page screenshot', async () => {
180
+ // A JS-intent flag guess (captured as a hidden decoy below): name the real
181
+ // flag before any other arg-shape check fires.
182
+ const decoy = ACTION_DECOY_FLAGS.find((f) => opts[f.slice(2)] !== undefined);
183
+ if (decoy) {
184
+ pageScreenshotCommand.error(`error: ${decoy} is not a flag on screenshot — use --action "<js>" to run JavaScript in the page ` +
185
+ `before the capture, e.g. gipity page screenshot "<url>" --action "document.getElementById('play').click()"`);
186
+ }
175
187
  // --wait is a hidden alias for --post-load-delay (agents reach for it because
176
188
  // sibling `page inspect`/`eval` name the flag --wait). Canonical name wins if
177
189
  // both given; fall back to the 1000ms default when neither is set.
@@ -308,6 +320,10 @@ export const pageScreenshotCommand = new Command('screenshot')
308
320
  console.log(`${label('Screenshot file')} ${success(savedFiles[i])}`);
309
321
  }
310
322
  }));
323
+ // Register the JS-intent guesses as hidden decoys (value-taking, so they swallow
324
+ // the script) — the action turns any of them into the "--action" redirect above.
325
+ for (const f of ACTION_DECOY_FLAGS)
326
+ pageScreenshotCommand.addOption(new Option(`${f} <value>`).hideHelp());
311
327
  // `screenshot` captures the page AFTER load + settle (+ optional --action). There
312
328
  // is no scroll-to-a-position or wait-for-a-selector lever (agents reach for
313
329
  // --scroll/--selector and get an unknown-option detour) — but `--full` does walk
@@ -1,8 +1,8 @@
1
1
  import { Command } from 'commander';
2
2
  import { readFileSync, existsSync, statSync } from 'fs';
3
- import { dirname, extname, relative } from 'path';
3
+ import { dirname, extname, relative, resolve } from 'path';
4
4
  import { post } from '../api.js';
5
- import { resolveProjectContext, getConfigPath, shouldIgnore } from '../config.js';
5
+ import { resolveProjectContext, getConfigPath, getProjectRoot, shouldIgnore } from '../config.js';
6
6
  import { SCRATCH_IGNORE } from '../setup.js';
7
7
  import { sync } from '../sync.js';
8
8
  import { error as clrError, dim } from '../colors.js';
@@ -344,10 +344,28 @@ GCC/Rust).
344
344
  console.error(res.data.stderr);
345
345
  if (res.data.timedOut)
346
346
  console.error(`[Timed out after ${res.data.durationMs}ms]`);
347
+ if (res.data.stdoutTruncated || res.data.stderrTruncated) {
348
+ console.error(dim('Note: output truncated at 256 KB. Write large results to a file instead of printing them.'));
349
+ }
347
350
  if (res.data.outputFiles && res.data.outputFiles.length > 0) {
348
- console.log(`\nOutput files ${pulledLocal ? 'synced to this directory' : 'saved to project'}:`);
349
- for (const f of res.data.outputFiles)
350
- console.log(`${f}`);
351
+ // Only claim a file is "synced to this directory" once it is actually on
352
+ // local disk - a sync that ran is not the same as a file that landed.
353
+ const projectRoot = getProjectRoot();
354
+ const landed = (f) => pulledLocal && !!projectRoot && existsSync(resolve(projectRoot, f));
355
+ const onDisk = res.data.outputFiles.filter(landed);
356
+ const notOnDisk = res.data.outputFiles.filter((f) => !landed(f));
357
+ if (onDisk.length > 0) {
358
+ console.log('\nOutput files synced to this directory:');
359
+ for (const f of onDisk)
360
+ console.log(`${f}`);
361
+ }
362
+ if (notOnDisk.length > 0) {
363
+ console.log('\nOutput files saved to project:');
364
+ for (const f of notOnDisk)
365
+ console.log(`${f}`);
366
+ if (pulledLocal)
367
+ console.log(dim("Not pulled locally - run 'gipity sync' to fetch them."));
368
+ }
351
369
  }
352
370
  if (res.data.skippedOutputFiles && res.data.skippedOutputFiles.length > 0) {
353
371
  console.log(dim('\nNot persisted (--no-sync-output):'));
@@ -40,7 +40,9 @@ function printStepRuns(steps, emptyNote) {
40
40
  for (const s of steps) {
41
41
  const statusColor = s.status === 'completed' ? success : s.status === 'failed' ? clrError : muted;
42
42
  const model = s.model_used ? ` ${muted(`[${s.model_used}]`)}` : '';
43
- console.log(` ${s.step_order}. ${statusColor(s.status)} ${s.tokens_used ?? 0} tokens${model}`);
43
+ // Name the step: "2. failed" alone doesn't say which one.
44
+ const name = s.step_name ? `${bold(s.step_name)} ` : '';
45
+ console.log(` ${s.step_order}. ${name}${statusColor(s.status)} ${s.tokens_used ?? 0} tokens${model}`);
44
46
  if (s.error_message)
45
47
  console.log(` ${clrError(s.error_message)}`);
46
48
  if (s.output_json !== null && s.output_json !== undefined) {
package/dist/index.js CHANGED
@@ -3944,10 +3944,10 @@ function prompt(question) {
3944
3944
  return Promise.reject(new Error(`prompt() called without a TTY: ${question.trim()}`));
3945
3945
  }
3946
3946
  const rl = createInterface({ input: process.stdin, output: process.stdout });
3947
- return new Promise((resolve16) => {
3947
+ return new Promise((resolve17) => {
3948
3948
  rl.question(question, (answer) => {
3949
3949
  rl.close();
3950
- resolve16(answer.trim());
3950
+ resolve17(answer.trim());
3951
3951
  });
3952
3952
  });
3953
3953
  }
@@ -3977,7 +3977,7 @@ async function confirm(question, opts = {}) {
3977
3977
  const wasRaw = stdin.isRaw ?? false;
3978
3978
  stdin.setRawMode(true);
3979
3979
  stdin.resume();
3980
- return new Promise((resolve16) => {
3980
+ return new Promise((resolve17) => {
3981
3981
  stdin.once("data", (key) => {
3982
3982
  stdin.setRawMode(wasRaw);
3983
3983
  stdin.pause();
@@ -3992,12 +3992,12 @@ async function confirm(question, opts = {}) {
3992
3992
  else if (k === "n") answer = false;
3993
3993
  else answer = defaultYes;
3994
3994
  console.log(answer ? "y" : "n");
3995
- resolve16(answer);
3995
+ resolve17(answer);
3996
3996
  });
3997
3997
  });
3998
3998
  }
3999
3999
  function pickOne(label2, max, defaultIdx = 1) {
4000
- return new Promise((resolve16) => {
4000
+ return new Promise((resolve17) => {
4001
4001
  process.stdout.write(` ${bold(label2)} (1-${max}) [${bold(String(defaultIdx))}]: `);
4002
4002
  const { stdin } = process;
4003
4003
  const wasRaw = stdin.isRaw ?? false;
@@ -4013,15 +4013,15 @@ function pickOne(label2, max, defaultIdx = 1) {
4013
4013
  }
4014
4014
  if (ch === "\r" || ch === "\n") {
4015
4015
  console.log(String(defaultIdx));
4016
- return resolve16(defaultIdx);
4016
+ return resolve17(defaultIdx);
4017
4017
  }
4018
4018
  const n = parseInt(ch, 10);
4019
4019
  if (n >= 1 && n <= max) {
4020
4020
  console.log(String(n));
4021
- return resolve16(n);
4021
+ return resolve17(n);
4022
4022
  }
4023
4023
  console.log(String(defaultIdx));
4024
- resolve16(defaultIdx);
4024
+ resolve17(defaultIdx);
4025
4025
  });
4026
4026
  });
4027
4027
  }
@@ -5370,8 +5370,8 @@ var require_streamx = __commonJS({
5370
5370
  return this;
5371
5371
  },
5372
5372
  next() {
5373
- return new Promise(function(resolve16, reject) {
5374
- promiseResolve = resolve16;
5373
+ return new Promise(function(resolve17, reject) {
5374
+ promiseResolve = resolve17;
5375
5375
  promiseReject = reject;
5376
5376
  const data = stream.read();
5377
5377
  if (data !== null) ondata(data);
@@ -5401,11 +5401,11 @@ var require_streamx = __commonJS({
5401
5401
  }
5402
5402
  function destroy(err) {
5403
5403
  stream.destroy(err);
5404
- return new Promise((resolve16, reject) => {
5405
- if (stream._duplexState & DESTROYED) return resolve16({ value: void 0, done: true });
5404
+ return new Promise((resolve17, reject) => {
5405
+ if (stream._duplexState & DESTROYED) return resolve17({ value: void 0, done: true });
5406
5406
  stream.once("close", function() {
5407
5407
  if (err) reject(err);
5408
- else resolve16({ value: void 0, done: true });
5408
+ else resolve17({ value: void 0, done: true });
5409
5409
  });
5410
5410
  });
5411
5411
  }
@@ -5449,8 +5449,8 @@ var require_streamx = __commonJS({
5449
5449
  const writes = pending + (ws._duplexState & WRITE_WRITING ? 1 : 0);
5450
5450
  if (writes === 0) return Promise.resolve(true);
5451
5451
  if (state.drains === null) state.drains = [];
5452
- return new Promise((resolve16) => {
5453
- state.drains.push({ writes, resolve: resolve16 });
5452
+ return new Promise((resolve17) => {
5453
+ state.drains.push({ writes, resolve: resolve17 });
5454
5454
  });
5455
5455
  }
5456
5456
  write(data) {
@@ -5555,10 +5555,10 @@ var require_streamx = __commonJS({
5555
5555
  cb(null);
5556
5556
  }
5557
5557
  function pipelinePromise(...streams) {
5558
- return new Promise((resolve16, reject) => {
5558
+ return new Promise((resolve17, reject) => {
5559
5559
  return pipeline(...streams, (err) => {
5560
5560
  if (err) return reject(err);
5561
- resolve16();
5561
+ resolve17();
5562
5562
  });
5563
5563
  });
5564
5564
  }
@@ -6213,16 +6213,16 @@ var require_extract = __commonJS({
6213
6213
  entryCallback = null;
6214
6214
  cb(err);
6215
6215
  }
6216
- function onnext(resolve16, reject) {
6216
+ function onnext(resolve17, reject) {
6217
6217
  if (error2) {
6218
6218
  return reject(error2);
6219
6219
  }
6220
6220
  if (entryStream) {
6221
- resolve16({ value: entryStream, done: false });
6221
+ resolve17({ value: entryStream, done: false });
6222
6222
  entryStream = null;
6223
6223
  return;
6224
6224
  }
6225
- promiseResolve = resolve16;
6225
+ promiseResolve = resolve17;
6226
6226
  promiseReject = reject;
6227
6227
  consumeCallback(null);
6228
6228
  if (extract3._finished && promiseResolve) {
@@ -6250,11 +6250,11 @@ var require_extract = __commonJS({
6250
6250
  function destroy(err) {
6251
6251
  extract3.destroy(err);
6252
6252
  consumeCallback(err);
6253
- return new Promise((resolve16, reject) => {
6254
- if (extract3.destroyed) return resolve16({ value: void 0, done: true });
6253
+ return new Promise((resolve17, reject) => {
6254
+ if (extract3.destroyed) return resolve17({ value: void 0, done: true });
6255
6255
  extract3.once("close", function() {
6256
6256
  if (err) reject(err);
6257
- else resolve16({ value: void 0, done: true });
6257
+ else resolve17({ value: void 0, done: true });
6258
6258
  });
6259
6259
  });
6260
6260
  }
@@ -6705,7 +6705,7 @@ async function postForTarEntries(path4, body, retried = false) {
6705
6705
  if (!res.body) throw new ApiError(500, "EMPTY_RESPONSE", "Server returned no body");
6706
6706
  const extract3 = tar.extract();
6707
6707
  const entries = [];
6708
- const done = new Promise((resolve16, reject) => {
6708
+ const done = new Promise((resolve17, reject) => {
6709
6709
  extract3.on("entry", (header, stream, next) => {
6710
6710
  const chunks = [];
6711
6711
  stream.on("data", (c) => chunks.push(c));
@@ -6716,7 +6716,7 @@ async function postForTarEntries(path4, body, retried = false) {
6716
6716
  stream.on("error", reject);
6717
6717
  stream.resume();
6718
6718
  });
6719
- extract3.on("finish", () => resolve16());
6719
+ extract3.on("finish", () => resolve17());
6720
6720
  extract3.on("error", reject);
6721
6721
  });
6722
6722
  Readable.fromWeb(res.body).pipe(extract3);
@@ -7176,7 +7176,7 @@ init_config();
7176
7176
  init_utils();
7177
7177
  import { readFileSync as readFileSync23 } from "fs";
7178
7178
  import { fileURLToPath as fileURLToPath3 } from "url";
7179
- import { dirname as dirname13, resolve as resolve15 } from "path";
7179
+ import { dirname as dirname13, resolve as resolve16 } from "path";
7180
7180
 
7181
7181
  // src/helpers/output.ts
7182
7182
  var frameOpen = false;
@@ -7616,25 +7616,25 @@ async function hashFile(path4) {
7616
7616
  const hash = createHash("sha256");
7617
7617
  let size = 0;
7618
7618
  const stream = createReadStream(path4);
7619
- await new Promise((resolve16, reject) => {
7619
+ await new Promise((resolve17, reject) => {
7620
7620
  stream.on("data", (chunk) => {
7621
7621
  const buf = chunk;
7622
7622
  hash.update(buf);
7623
7623
  size += buf.length;
7624
7624
  });
7625
- stream.on("end", () => resolve16());
7625
+ stream.on("end", () => resolve17());
7626
7626
  stream.on("error", reject);
7627
7627
  });
7628
7628
  return { sha256: hash.digest("hex"), size };
7629
7629
  }
7630
7630
  function readRange(path4, start, end) {
7631
- return new Promise((resolve16, reject) => {
7631
+ return new Promise((resolve17, reject) => {
7632
7632
  const chunks = [];
7633
7633
  const stream = createReadStream(path4, { start, end });
7634
7634
  stream.on("data", (c) => {
7635
7635
  chunks.push(c);
7636
7636
  });
7637
- stream.on("end", () => resolve16(Buffer.concat(chunks)));
7637
+ stream.on("end", () => resolve17(Buffer.concat(chunks)));
7638
7638
  stream.on("error", reject);
7639
7639
  });
7640
7640
  }
@@ -8326,7 +8326,7 @@ async function fetchRemote(projectGuid) {
8326
8326
  function extractTarToMap(stream, idleMs, onBytes, keep) {
8327
8327
  const extract3 = tar2.extract();
8328
8328
  const files = /* @__PURE__ */ new Map();
8329
- return new Promise((resolve16, reject) => {
8329
+ return new Promise((resolve17, reject) => {
8330
8330
  let settled = false;
8331
8331
  let idle;
8332
8332
  const arm = () => {
@@ -8362,7 +8362,7 @@ function extractTarToMap(stream, idleMs, onBytes, keep) {
8362
8362
  });
8363
8363
  entryStream.resume();
8364
8364
  });
8365
- extract3.on("finish", () => done(resolve16, files));
8365
+ extract3.on("finish", () => done(resolve17, files));
8366
8366
  extract3.on("error", (e) => done(reject, e));
8367
8367
  stream.on("error", (e) => done(reject, e));
8368
8368
  arm();
@@ -10719,7 +10719,14 @@ function splitCsv(values) {
10719
10719
  function appendOption(value, previous = []) {
10720
10720
  return [...previous, value];
10721
10721
  }
10722
+ var ACTION_DECOY_FLAGS = ["--eval", "--js", "--javascript", "--script", "--code", "--exec"];
10722
10723
  var pageScreenshotCommand = new Command("screenshot").description("Screenshot a web page").argument("<url>", "URL to screenshot").option("--post-load-delay <ms>", "Delay after DOMContentLoaded before capture, in ms (default: 1000)").option("--action <js>", `Run JS in the page before capturing \u2014 e.g. click a button to enter a state ("document.getElementById('play').click()"). Runs as an async function body, so const/await and app-relative import('./\u2026') work. Runs after the post-load delay, then settles again before the shot. If it throws, the capture still happens and the failure is reported.`).option("--full", "Capture the full scrollable page (default: viewport only). Scrolls the page through first so scroll-reveal/fade-in-on-scroll (IntersectionObserver) sections render into the shot instead of capturing blank.").option("-o, --output <file>", "Output path (single viewport only; default .gipity/screenshots/ss-<host>-<timestamp>.png)").option("--device <names>", `Device preset(s): ${Object.keys(DEVICE_PRESETS).join(", ")} (comma-separated or repeat flag). mobile/tablet emulate a real touch device \u2014 touch events, mobile user-agent, DPR \u2014 so touch-gated mobile UI actually renders.`, appendOption, []).option("--viewport <dims>", "Raw viewport(s): WxH or WxH@dpr (comma-separated or repeat flag)", appendOption, []).option("--no-reload-between", "Skip reload between viewports (faster, lower fidelity - only safe for static pages)").option("--fake-media", "Grant a synthetic microphone + camera and auto-accept the getUserMedia prompt, so voice/camera apps render headlessly (audio is a built-in tone, not real speech)").option("--auth", "Capture the page signed in as you (your Gipity account), so UI behind a Sign-in-with-Gipity login is shown. Only works for apps using Sign in with Gipity, hosted on *.gipity.ai.").option("--json", "Output JSON metadata instead of a friendly summary").addOption(new Option("--wait <ms>", "Alias for --post-load-delay").hideHelp()).addOption(new Option("--full-page", "Alias for --full").hideHelp()).action((url, opts) => run("Page screenshot", async () => {
10724
+ const decoy = ACTION_DECOY_FLAGS.find((f) => opts[f.slice(2)] !== void 0);
10725
+ if (decoy) {
10726
+ pageScreenshotCommand.error(
10727
+ `error: ${decoy} is not a flag on screenshot \u2014 use --action "<js>" to run JavaScript in the page before the capture, e.g. gipity page screenshot "<url>" --action "document.getElementById('play').click()"`
10728
+ );
10729
+ }
10723
10730
  const delayRaw = opts.postLoadDelay ?? opts.wait ?? "1000";
10724
10731
  const postLoadDelayMs = delayRaw !== void 0 ? parseInt(String(delayRaw), 10) : void 0;
10725
10732
  if (postLoadDelayMs !== void 0 && (!Number.isFinite(postLoadDelayMs) || postLoadDelayMs < 0)) {
@@ -10829,6 +10836,7 @@ ${brand("@ " + dims)}${deviceTag}`);
10829
10836
  console.log(`${label("Screenshot file")} ${success(savedFiles[i])}`);
10830
10837
  }
10831
10838
  }));
10839
+ for (const f of ACTION_DECOY_FLAGS) pageScreenshotCommand.addOption(new Option(`${f} <value>`).hideHelp());
10832
10840
  pageScreenshotCommand.addHelpText("after", `
10833
10841
  Examples:
10834
10842
  gipity page screenshot "https://dev.gipity.ai/me/app/"
@@ -11294,7 +11302,7 @@ memoryCommand.command("delete <topic>").description("Delete a topic").option("--
11294
11302
  init_api();
11295
11303
  init_config();
11296
11304
  import { readFileSync as readFileSync12, existsSync as existsSync9, statSync as statSync6 } from "fs";
11297
- import { dirname as dirname8, extname as extname4, relative as relative3 } from "path";
11305
+ import { dirname as dirname8, extname as extname4, relative as relative3, resolve as resolve11 } from "path";
11298
11306
  init_colors();
11299
11307
  var LANG_MAP = {
11300
11308
  js: "javascript",
@@ -11541,10 +11549,23 @@ GCC/Rust).
11541
11549
  if (res.data.stdout) console.log(res.data.stdout);
11542
11550
  if (res.data.stderr) console.error(res.data.stderr);
11543
11551
  if (res.data.timedOut) console.error(`[Timed out after ${res.data.durationMs}ms]`);
11552
+ if (res.data.stdoutTruncated || res.data.stderrTruncated) {
11553
+ console.error(dim("Note: output truncated at 256 KB. Write large results to a file instead of printing them."));
11554
+ }
11544
11555
  if (res.data.outputFiles && res.data.outputFiles.length > 0) {
11545
- console.log(`
11546
- Output files ${pulledLocal ? "synced to this directory" : "saved to project"}:`);
11547
- for (const f of res.data.outputFiles) console.log(`${f}`);
11556
+ const projectRoot = getProjectRoot();
11557
+ const landed = (f) => pulledLocal && !!projectRoot && existsSync9(resolve11(projectRoot, f));
11558
+ const onDisk = res.data.outputFiles.filter(landed);
11559
+ const notOnDisk = res.data.outputFiles.filter((f) => !landed(f));
11560
+ if (onDisk.length > 0) {
11561
+ console.log("\nOutput files synced to this directory:");
11562
+ for (const f of onDisk) console.log(`${f}`);
11563
+ }
11564
+ if (notOnDisk.length > 0) {
11565
+ console.log("\nOutput files saved to project:");
11566
+ for (const f of notOnDisk) console.log(`${f}`);
11567
+ if (pulledLocal) console.log(dim("Not pulled locally - run 'gipity sync' to fetch them."));
11568
+ }
11548
11569
  }
11549
11570
  if (res.data.skippedOutputFiles && res.data.skippedOutputFiles.length > 0) {
11550
11571
  console.log(dim("\nNot persisted (--no-sync-output):"));
@@ -11955,7 +11976,8 @@ function printStepRuns(steps, emptyNote) {
11955
11976
  for (const s of steps) {
11956
11977
  const statusColor = s.status === "completed" ? success : s.status === "failed" ? error : muted;
11957
11978
  const model = s.model_used ? ` ${muted(`[${s.model_used}]`)}` : "";
11958
- console.log(` ${s.step_order}. ${statusColor(s.status)} ${s.tokens_used ?? 0} tokens${model}`);
11979
+ const name = s.step_name ? `${bold(s.step_name)} ` : "";
11980
+ console.log(` ${s.step_order}. ${name}${statusColor(s.status)} ${s.tokens_used ?? 0} tokens${model}`);
11959
11981
  if (s.error_message) console.log(` ${error(s.error_message)}`);
11960
11982
  if (s.output_json !== null && s.output_json !== void 0) {
11961
11983
  console.log(JSON.stringify(s.output_json, null, 2).split("\n").map((l) => ` ${l}`).join("\n"));
@@ -11963,7 +11985,7 @@ function printStepRuns(steps, emptyNote) {
11963
11985
  }
11964
11986
  }
11965
11987
  var TERMINAL_RUN_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
11966
- var sleep2 = (ms) => new Promise((resolve16) => setTimeout(resolve16, ms));
11988
+ var sleep2 = (ms) => new Promise((resolve17) => setTimeout(resolve17, ms));
11967
11989
  async function waitForRun(wfGuid, prevGuid, timeoutSec) {
11968
11990
  const deadline = Date.now() + timeoutSec * 1e3;
11969
11991
  let runGuid;
@@ -12556,7 +12578,7 @@ storageCommand.command("retention").description("View or adjust your file versio
12556
12578
  init_platform();
12557
12579
  init_auth();
12558
12580
  init_api();
12559
- import { join as join14, dirname as dirname10, resolve as resolve12, basename as basename4, sep as sep4 } from "path";
12581
+ import { join as join14, dirname as dirname10, resolve as resolve13, basename as basename4, sep as sep4 } from "path";
12560
12582
  import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync9, renameSync as renameSync2, readdirSync as readdirSync5, statSync as statSync7 } from "fs";
12561
12583
  import { homedir as homedir10 } from "os";
12562
12584
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -12871,7 +12893,7 @@ function windowsTaskPlan(cliPath) {
12871
12893
  init_platform();
12872
12894
  init_api();
12873
12895
  import { hostname as hostname3, userInfo, platform as osPlatform2 } from "os";
12874
- import { resolve as resolve11, dirname as dirname9 } from "path";
12896
+ import { resolve as resolve12, dirname as dirname9 } from "path";
12875
12897
  import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "fs";
12876
12898
 
12877
12899
  // src/relay/machine-id.ts
@@ -12933,7 +12955,7 @@ function friendlyDeviceName() {
12933
12955
  return host || `My ${kind}`;
12934
12956
  }
12935
12957
  function resolveCliPath() {
12936
- return resolve11(process.argv[1] ?? "gipity");
12958
+ return resolve12(process.argv[1] ?? "gipity");
12937
12959
  }
12938
12960
  async function pairDevice(opts = {}) {
12939
12961
  const existing = getDevice();
@@ -13649,7 +13671,7 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
13649
13671
  let existing = getConfig();
13650
13672
  let forceAdoptCwd = false;
13651
13673
  if (existing && !opts.newProject && !opts.project && !nonInteractive) {
13652
- const cwdHasConfig = existsSync11(resolve12(process.cwd(), ".gipity.json"));
13674
+ const cwdHasConfig = existsSync11(resolve13(process.cwd(), ".gipity.json"));
13653
13675
  if (!cwdHasConfig && isLikelyEmpty(process.cwd())) {
13654
13676
  const ancestorRoot = dirname10(getConfigPath());
13655
13677
  console.log(` ${bold("You are inside an existing Gipity project.")}
@@ -14323,7 +14345,8 @@ var addCommand = new Command("add").description("Add a template or kit").argumen
14323
14345
  };
14324
14346
  }
14325
14347
  if (!opts.json && !process.stdout.isTTY) {
14326
- console.error(muted("Installing (server writes files + generates favicons; first add for a title can take ~10s)..."));
14348
+ const isKit = KITS.some((k) => k.key === name);
14349
+ console.error(muted(isKit ? "Installing kit (server-side install pipeline)..." : "Installing (server writes files + generates favicons; first add for a title can take ~10s)..."));
14327
14350
  }
14328
14351
  const doAdd = () => post(`/projects/${config.projectGuid}/add`, body);
14329
14352
  const res = opts.json ? await doAdd() : await withSpinner("Installing\u2026", doAdd, { done: null });
@@ -14645,7 +14668,7 @@ logsCommand.command("fn <name>").description("Show function logs").option("--lim
14645
14668
  for (const log3 of res.data) {
14646
14669
  const time = new Date(log3.created_at).toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit" });
14647
14670
  const dur = log3.duration_ms !== null ? `${log3.duration_ms}ms`.padEnd(8) : "".padEnd(8);
14648
- const statusColor = log3.status === "success" ? success : log3.status === "error" ? error : warning;
14671
+ const statusColor = log3.status === "ok" ? success : log3.status === "error" ? error : warning;
14649
14672
  const status = statusColor(log3.status.padEnd(8));
14650
14673
  const trigger = muted(log3.trigger_type.padEnd(8));
14651
14674
  const err = log3.error_message ? ` ${error(`"${log3.error_message}"`)}` : "";
@@ -15722,9 +15745,9 @@ jobCommand.command("cancel <runGuid>").description("Cancel a queued or running j
15722
15745
  }));
15723
15746
  jobCommand.command("run-local <name>").description("Run the job in a local Docker container against your local filesystem (no platform round-trip)").option("--image <image>", "Docker image to run inside", "easyclaw/sandbox:latest").option("--no-deps", "Skip pip/npm install even if a deps file is present").action(async (name, opts) => {
15724
15747
  const { existsSync: existsSync20, statSync: statSync9 } = await import("node:fs");
15725
- const { resolve: resolve16, join: join22 } = await import("node:path");
15748
+ const { resolve: resolve17, join: join22 } = await import("node:path");
15726
15749
  const { spawnCommand: spawnCommand2, spawnSyncCommand: spawnSyncCommand2 } = await Promise.resolve().then(() => (init_platform(), platform_exports));
15727
- const jobDir = resolve16(process.cwd(), "jobs", name);
15750
+ const jobDir = resolve17(process.cwd(), "jobs", name);
15728
15751
  if (!existsSync20(jobDir) || !statSync9(jobDir).isDirectory()) {
15729
15752
  console.error(error(`jobs/${name}/ not found in current directory`));
15730
15753
  process.exit(1);
@@ -16336,14 +16359,14 @@ var domainCommand = new Command("domain").description("Manage custom domains").a
16336
16359
 
16337
16360
  // src/commands/realtime.ts
16338
16361
  import { existsSync as existsSync13 } from "fs";
16339
- import { dirname as dirname12, resolve as resolve13 } from "path";
16362
+ import { dirname as dirname12, resolve as resolve14 } from "path";
16340
16363
  init_api();
16341
16364
  init_config();
16342
16365
  init_colors();
16343
16366
  function hasDeployManifest() {
16344
16367
  const cfgPath = getConfigPath();
16345
16368
  if (!cfgPath) return false;
16346
- return existsSync13(resolve13(dirname12(cfgPath), "gipity.yaml"));
16369
+ return existsSync13(resolve14(dirname12(cfgPath), "gipity.yaml"));
16347
16370
  }
16348
16371
  var roomCommand = new Command("room").description("Manage realtime rooms").argument("[action]", "list | create | delete | info", "list").argument("[name]", "room name (for create | delete | info)").option("--type <type>", "room type for create: state | relay", "state").option("--auth <level>", "auth level for create: public | user", "public").option("--max-clients <n>", "max clients for create (1-200)").option("--json", "Output as JSON").action((action, name, opts) => run("Realtime room", async () => {
16349
16372
  const config = requireConfig();
@@ -16519,7 +16542,7 @@ async function pollTestStatus(projectGuid, runGuid, opts) {
16519
16542
  }
16520
16543
  }
16521
16544
  }
16522
- await new Promise((resolve16) => setTimeout(resolve16, getPollInterval()));
16545
+ await new Promise((resolve17) => setTimeout(resolve17, getPollInterval()));
16523
16546
  }
16524
16547
  }
16525
16548
  var testCommand = new Command("test").description("Run tests").argument("[path]", 'Test path filter (e.g. "api", "e2e/portal")').option("--filter <path>", "Alias for the positional test path filter").option("--timeout <ms>", "Per-test timeout in ms", "30000").option("--retry <n>", "Retry failed tests N times", "0").option("--no-sync", "Skip sync-up before tests").option("--json", "Output as JSON").action((pathFilter, opts) => run("Test", async () => {
@@ -17604,13 +17627,13 @@ function parseVersion(out) {
17604
17627
  return m ? m[0] : void 0;
17605
17628
  }
17606
17629
  function runProbe(cmd, args) {
17607
- return new Promise((resolve16) => {
17630
+ return new Promise((resolve17) => {
17608
17631
  let out = "";
17609
17632
  let settled = false;
17610
17633
  const done = (s) => {
17611
17634
  if (!settled) {
17612
17635
  settled = true;
17613
- resolve16(s);
17636
+ resolve17(s);
17614
17637
  }
17615
17638
  };
17616
17639
  let child;
@@ -17997,9 +18020,9 @@ async function dispatchLoop(ctx, opts) {
17997
18020
  while (inflight.size >= MAX_CONCURRENT_DISPATCHES && !ctx.abort.signal.aborted) {
17998
18021
  await Promise.race([
17999
18022
  ...inflight,
18000
- new Promise((resolve16) => {
18001
- if (ctx.abort.signal.aborted) return resolve16();
18002
- ctx.abort.signal.addEventListener("abort", () => resolve16(), { once: true });
18023
+ new Promise((resolve17) => {
18024
+ if (ctx.abort.signal.aborted) return resolve17();
18025
+ ctx.abort.signal.addEventListener("abort", () => resolve17(), { once: true });
18003
18026
  })
18004
18027
  ]);
18005
18028
  }
@@ -18484,7 +18507,7 @@ async function killRunningForConv(convGuid) {
18484
18507
  }
18485
18508
  }
18486
18509
  const graceTimers = [];
18487
- const escalate = new Promise((resolve16) => {
18510
+ const escalate = new Promise((resolve17) => {
18488
18511
  const t = setTimeout(() => {
18489
18512
  for (const e of matches) {
18490
18513
  log2("warn", "previous dispatch ignored SIGTERM - escalating to SIGKILL", { conv: convGuid });
@@ -18493,7 +18516,7 @@ async function killRunningForConv(convGuid) {
18493
18516
  } catch {
18494
18517
  }
18495
18518
  }
18496
- resolve16();
18519
+ resolve17();
18497
18520
  }, KILL_GRACE_MS);
18498
18521
  graceTimers.push(t);
18499
18522
  });
@@ -18503,7 +18526,7 @@ async function killRunningForConv(convGuid) {
18503
18526
  }
18504
18527
  async function spawnSync2(cwd, timeoutMs, onSpawn) {
18505
18528
  const cmd = process.env.GIPITY_RELAY_CLAUDE_CMD || resolveCommand("gipity");
18506
- return new Promise((resolve16, reject) => {
18529
+ return new Promise((resolve17, reject) => {
18507
18530
  const child = spawnCommand(cmd, ["sync", "--json"], {
18508
18531
  cwd,
18509
18532
  env: childEnv(),
@@ -18543,7 +18566,7 @@ async function spawnSync2(cwd, timeoutMs, onSpawn) {
18543
18566
  child.on("exit", (code) => finish(() => {
18544
18567
  if (code === 0) {
18545
18568
  log2("info", "sync done", { cwd, stdoutLen });
18546
- resolve16();
18569
+ resolve17();
18547
18570
  } else {
18548
18571
  reject(new Error(`gipity sync exited ${code}${stderrBuf ? `: ${stderrBuf.trim().slice(0, 300)}` : ""}`));
18549
18572
  }
@@ -18554,7 +18577,7 @@ async function spawnGipityClaude(args, cwd, d, queue, meta) {
18554
18577
  const cmd = process.env.GIPITY_RELAY_CLAUDE_CMD || resolveCommand("gipity");
18555
18578
  const fullArgs = [...args, "--output-format", "stream-json", "--verbose", "--include-partial-messages"];
18556
18579
  const env = childEnv({ GIPITY_CONVERSATION_GUID: d.conversation_guid, GIPITY_CAPTURE: "off" });
18557
- return new Promise((resolve16, reject) => {
18580
+ return new Promise((resolve17, reject) => {
18558
18581
  const child = spawnCommand(cmd, fullArgs, { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
18559
18582
  let resolveExited = () => {
18560
18583
  };
@@ -18720,7 +18743,7 @@ async function spawnGipityClaude(args, cwd, d, queue, meta) {
18720
18743
  void postProgress(d.conversation_guid, buildProgressPayload(false));
18721
18744
  if (ownQueue) await q.close();
18722
18745
  cleanup();
18723
- resolve16({
18746
+ resolve17({
18724
18747
  exitCode: code ?? 1,
18725
18748
  killed,
18726
18749
  runtimeLimit,
@@ -18734,10 +18757,10 @@ async function runDispatchSync(d, cwd) {
18734
18757
  let killed = false;
18735
18758
  try {
18736
18759
  await spawnSync2(cwd, PROJECT_SYNC_TIMEOUT_MS, (child) => {
18737
- const exited = new Promise((resolve16) => {
18760
+ const exited = new Promise((resolve17) => {
18738
18761
  child.once("exit", (_code, signal) => {
18739
18762
  if (signal === "SIGTERM") killed = true;
18740
- resolve16();
18763
+ resolve17();
18741
18764
  });
18742
18765
  });
18743
18766
  running.set(d.short_guid, { child, convGuid: d.conversation_guid, exited });
@@ -18761,18 +18784,18 @@ function killDispatch(shortGuid) {
18761
18784
  }
18762
18785
  }
18763
18786
  function sleep4(ms, signal) {
18764
- return new Promise((resolve16) => {
18765
- if (signal?.aborted) return resolve16();
18787
+ return new Promise((resolve17) => {
18788
+ if (signal?.aborted) return resolve17();
18766
18789
  let onAbort = null;
18767
18790
  const t = setTimeout(() => {
18768
18791
  if (onAbort && signal) signal.removeEventListener("abort", onAbort);
18769
- resolve16();
18792
+ resolve17();
18770
18793
  }, ms);
18771
18794
  if (signal) {
18772
18795
  onAbort = () => {
18773
18796
  clearTimeout(t);
18774
18797
  signal.removeEventListener("abort", onAbort);
18775
- resolve16();
18798
+ resolve17();
18776
18799
  };
18777
18800
  signal.addEventListener("abort", onAbort, { once: true });
18778
18801
  }
@@ -19165,7 +19188,7 @@ init_utils();
19165
19188
  init_colors();
19166
19189
  import { existsSync as existsSync19, rmSync, unlinkSync as unlinkSync6, readFileSync as readFileSync21, writeFileSync as writeFileSync13 } from "fs";
19167
19190
  import { homedir as homedir14, platform as osPlatform4 } from "os";
19168
- import { join as join20, resolve as resolve14 } from "path";
19191
+ import { join as join20, resolve as resolve15 } from "path";
19169
19192
  function removeGipityPluginConfig() {
19170
19193
  const settingsPath = join20(homedir14(), ".claude", "settings.json");
19171
19194
  if (!existsSync19(settingsPath)) return false;
@@ -19220,7 +19243,7 @@ function tildify2(p) {
19220
19243
  return p === home || p.startsWith(home + "/") ? "~" + p.slice(home.length) : p;
19221
19244
  }
19222
19245
  function resolveCliPath2() {
19223
- return resolve14(process.argv[1] ?? "gipity");
19246
+ return resolve15(process.argv[1] ?? "gipity");
19224
19247
  }
19225
19248
  async function stopDaemon() {
19226
19249
  if (!isDaemonRunning()) return;
@@ -19892,11 +19915,11 @@ function installOutputTrace(label2) {
19892
19915
  // src/index.ts
19893
19916
  installOutputTrace("index");
19894
19917
  var __dirname = dirname13(fileURLToPath3(import.meta.url));
19895
- var pkg = JSON.parse(readFileSync23(resolve15(__dirname, "../package.json"), "utf-8"));
19918
+ var pkg = JSON.parse(readFileSync23(resolve16(__dirname, "../package.json"), "utf-8"));
19896
19919
  function versionLabel() {
19897
19920
  const base = `v${pkg.version}`;
19898
19921
  try {
19899
- const info2 = JSON.parse(readFileSync23(resolve15(__dirname, "build-info.json"), "utf-8"));
19922
+ const info2 = JSON.parse(readFileSync23(resolve16(__dirname, "build-info.json"), "utf-8"));
19900
19923
  if (info2?.sha) return `${base} (dev ${info2.sha}${info2.dirty ? ", modified" : ""})`;
19901
19924
  } catch {
19902
19925
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gipity",
3
- "version": "1.0.425",
3
+ "version": "1.0.426",
4
4
  "description": "The full-stack platform tuned for AI agents. Database, storage, auth, functions, deploy, and drop-in kits - all agent-tuned. Pair with Claude Code or use standalone.",
5
5
  "bin": {
6
6
  "gipity": "dist/updater/shim.js",