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.
- package/dist/commands/add.js +6 -1
- package/dist/commands/logs.js +4 -1
- package/dist/commands/page-screenshot.js +16 -0
- package/dist/commands/sandbox.js +23 -5
- package/dist/commands/workflow.js +3 -1
- package/dist/index.js +93 -70
- package/package.json +1 -1
package/dist/commands/add.js
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/commands/logs.js
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/commands/sandbox.js
CHANGED
|
@@ -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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
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((
|
|
3947
|
+
return new Promise((resolve17) => {
|
|
3948
3948
|
rl.question(question, (answer) => {
|
|
3949
3949
|
rl.close();
|
|
3950
|
-
|
|
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((
|
|
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
|
-
|
|
3995
|
+
resolve17(answer);
|
|
3996
3996
|
});
|
|
3997
3997
|
});
|
|
3998
3998
|
}
|
|
3999
3999
|
function pickOne(label2, max, defaultIdx = 1) {
|
|
4000
|
-
return new Promise((
|
|
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
|
|
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
|
|
4021
|
+
return resolve17(n);
|
|
4022
4022
|
}
|
|
4023
4023
|
console.log(String(defaultIdx));
|
|
4024
|
-
|
|
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(
|
|
5374
|
-
promiseResolve =
|
|
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((
|
|
5405
|
-
if (stream._duplexState & DESTROYED) return
|
|
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
|
|
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((
|
|
5453
|
-
state.drains.push({ writes, resolve:
|
|
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((
|
|
5558
|
+
return new Promise((resolve17, reject) => {
|
|
5559
5559
|
return pipeline(...streams, (err) => {
|
|
5560
5560
|
if (err) return reject(err);
|
|
5561
|
-
|
|
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(
|
|
6216
|
+
function onnext(resolve17, reject) {
|
|
6217
6217
|
if (error2) {
|
|
6218
6218
|
return reject(error2);
|
|
6219
6219
|
}
|
|
6220
6220
|
if (entryStream) {
|
|
6221
|
-
|
|
6221
|
+
resolve17({ value: entryStream, done: false });
|
|
6222
6222
|
entryStream = null;
|
|
6223
6223
|
return;
|
|
6224
6224
|
}
|
|
6225
|
-
promiseResolve =
|
|
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((
|
|
6254
|
-
if (extract3.destroyed) return
|
|
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
|
|
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((
|
|
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", () =>
|
|
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
|
|
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((
|
|
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", () =>
|
|
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((
|
|
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", () =>
|
|
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((
|
|
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(
|
|
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
|
-
|
|
11546
|
-
|
|
11547
|
-
|
|
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
|
-
|
|
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((
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
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 === "
|
|
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:
|
|
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 =
|
|
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
|
|
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(
|
|
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((
|
|
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((
|
|
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
|
-
|
|
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((
|
|
18001
|
-
if (ctx.abort.signal.aborted) return
|
|
18002
|
-
ctx.abort.signal.addEventListener("abort", () =>
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
18760
|
+
const exited = new Promise((resolve17) => {
|
|
18738
18761
|
child.once("exit", (_code, signal) => {
|
|
18739
18762
|
if (signal === "SIGTERM") killed = true;
|
|
18740
|
-
|
|
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((
|
|
18765
|
-
if (signal?.aborted) return
|
|
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
|
-
|
|
18792
|
+
resolve17();
|
|
18770
18793
|
}, ms);
|
|
18771
18794
|
if (signal) {
|
|
18772
18795
|
onAbort = () => {
|
|
18773
18796
|
clearTimeout(t);
|
|
18774
18797
|
signal.removeEventListener("abort", onAbort);
|
|
18775
|
-
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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.
|
|
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",
|