gipity 1.0.392 → 1.0.395
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/colors.js +22 -7
- package/dist/commands/agent.js +92 -1
- package/dist/commands/db.js +6 -2
- package/dist/commands/deploy.js +2 -1
- package/dist/commands/fn.js +6 -1
- package/dist/commands/generate.js +5 -0
- package/dist/commands/page-eval.js +20 -1
- package/dist/commands/page-inspect.js +34 -0
- package/dist/commands/page-screenshot.js +16 -11
- package/dist/commands/sandbox.js +11 -0
- package/dist/helpers/index.js +1 -1
- package/dist/helpers/output.js +23 -0
- package/dist/knowledge.js +18 -5
- package/dist/relay/state.js +8 -2
- package/package.json +2 -2
package/dist/colors.js
CHANGED
|
@@ -47,10 +47,14 @@ function detectColorLevel() {
|
|
|
47
47
|
return 2;
|
|
48
48
|
if (/-256(color)?$/.test(term) || term.includes('256'))
|
|
49
49
|
return 2;
|
|
50
|
-
//
|
|
51
|
-
// conhost
|
|
50
|
+
// Windows Terminal / VS Code set COLORTERM=truecolor (caught above). The
|
|
51
|
+
// classic console host (conhost, the standalone PowerShell window) advertises
|
|
52
|
+
// nothing, but every still-supported Windows build (10 v1703+, 2017) handles
|
|
53
|
+
// 256-color VT sequences reliably - so 256 is the safe floor. Orange then
|
|
54
|
+
// downscales to xterm-214 (a real orange) instead of the bright-yellow the
|
|
55
|
+
// 16-color palette is forced into (it has no orange slot at all).
|
|
52
56
|
if (process.platform === 'win32')
|
|
53
|
-
return
|
|
57
|
+
return 2;
|
|
54
58
|
if (term)
|
|
55
59
|
return 1;
|
|
56
60
|
return 0;
|
|
@@ -60,7 +64,8 @@ const COLOR_LEVEL = detectColorLevel();
|
|
|
60
64
|
const identity = (s) => s;
|
|
61
65
|
// ── RGB downgrade helpers ───────────────────────────────────────────────
|
|
62
66
|
// RGB → nearest xterm-256 palette index (6x6x6 cube + grayscale ramp).
|
|
63
|
-
|
|
67
|
+
// Exported for unit tests that pin the documented downscale (orange → 214).
|
|
68
|
+
export function rgbTo256(r, g, b) {
|
|
64
69
|
if (r === g && g === b) {
|
|
65
70
|
if (r < 8)
|
|
66
71
|
return 16;
|
|
@@ -74,7 +79,9 @@ function rgbTo256(r, g, b) {
|
|
|
74
79
|
Math.round((b / 255) * 5));
|
|
75
80
|
}
|
|
76
81
|
// RGB → nearest 16-color SGR foreground code (30-37 / 90-97).
|
|
77
|
-
|
|
82
|
+
// Exported for unit tests that pin the documented downscale (orange → bright
|
|
83
|
+
// yellow, i.e. the palette has no orange slot).
|
|
84
|
+
export function rgbTo16(r, g, b) {
|
|
78
85
|
const value = Math.round((Math.max(r, g, b) / 255) * 3);
|
|
79
86
|
if (value === 0)
|
|
80
87
|
return 30;
|
|
@@ -87,7 +94,13 @@ function rgbTo16(r, g, b) {
|
|
|
87
94
|
return code;
|
|
88
95
|
}
|
|
89
96
|
// ── Low-level builders ──────────────────────────────────────────────────
|
|
90
|
-
|
|
97
|
+
// `lowColorFallback` controls the 16-color (level 1) tier. The ANSI 16-color
|
|
98
|
+
// palette has no orange and only crude approximations of off-palette hues, so a
|
|
99
|
+
// color that can't be represented reads as a bug (brand orange → bright yellow).
|
|
100
|
+
// Off-palette brand colors pass a monochrome fallback (e.g. `bold`) to convey
|
|
101
|
+
// emphasis without a wrong hue; semantic colors that map cleanly (red, green,
|
|
102
|
+
// blue) omit it and keep their nearest-16 approximation.
|
|
103
|
+
export function makeFg(r, g, b, lowColorFallback) {
|
|
91
104
|
if (COLOR_LEVEL === 0)
|
|
92
105
|
return identity;
|
|
93
106
|
if (COLOR_LEVEL === 3) {
|
|
@@ -97,6 +110,8 @@ export function makeFg(r, g, b) {
|
|
|
97
110
|
const n = rgbTo256(r, g, b);
|
|
98
111
|
return (s) => `${ESC}[38;5;${n}m${s}${ESC}[39m`;
|
|
99
112
|
}
|
|
113
|
+
if (lowColorFallback)
|
|
114
|
+
return lowColorFallback;
|
|
100
115
|
const code = rgbTo16(r, g, b);
|
|
101
116
|
return (s) => `${ESC}[${code}m${s}${ESC}[39m`;
|
|
102
117
|
}
|
|
@@ -130,7 +145,7 @@ export const underline = makeStyle(4, 24);
|
|
|
130
145
|
// ── Gipity platform palette ────────────────────────────────────────────
|
|
131
146
|
// Colors sourced from platform/client/src/css/styles.css and
|
|
132
147
|
// platform/apps/gipitsm/src/css/tokens.css
|
|
133
|
-
export const brand = makeFg(254, 166, 14); // Gipity orange #fea60e
|
|
148
|
+
export const brand = makeFg(254, 166, 14, bold); // Gipity orange #fea60e (→ bold on 16-color; no orange in palette)
|
|
134
149
|
export const error = makeFg(239, 68, 68); // #ef4444
|
|
135
150
|
export const warning = makeFg(245, 158, 11); // #f59e0b
|
|
136
151
|
export const success = makeFg(34, 197, 94); // #22c55e
|
package/dist/commands/agent.js
CHANGED
|
@@ -68,12 +68,103 @@ agentCommand
|
|
|
68
68
|
else if (field === 'temp' || field === 'temperature')
|
|
69
69
|
body.temperature = parseFloat(value);
|
|
70
70
|
else {
|
|
71
|
-
console.error(clrError(`Unknown field: ${field}. Use: model, temp`));
|
|
71
|
+
console.error(clrError(`Unknown field: ${field}. Use: model, temp (for soul/goal use \`gipity agent soul|goal\`)`));
|
|
72
72
|
process.exit(1);
|
|
73
73
|
}
|
|
74
74
|
await put(`/agents/${config.agentGuid}`, body);
|
|
75
75
|
printResult(`Set ${field} = ${value}`, opts, { success: true, field, value });
|
|
76
76
|
}));
|
|
77
|
+
/** The active agent's guid, or a clear error - the brain commands all need one. */
|
|
78
|
+
function requireAgentGuid() {
|
|
79
|
+
const config = requireConfig();
|
|
80
|
+
if (!config.agentGuid) {
|
|
81
|
+
console.error(clrError('No active agent. Switch to one with: gipity agent <name>'));
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
return config.agentGuid;
|
|
85
|
+
}
|
|
86
|
+
// --- Brain: soul / goal / rules / learn ---
|
|
87
|
+
// These hit the account-scoped /account/agents surface (the same dual-auth,
|
|
88
|
+
// app-callable routes a deployed app uses), so the CLI, the web terminal, and an
|
|
89
|
+
// app all drive the agent's brain through one set of endpoints. No more
|
|
90
|
+
// hand-rolled `curl -X PUT a.gipity.ai/agents/:guid/soul` with a scraped token.
|
|
91
|
+
agentCommand
|
|
92
|
+
.command('soul [text...]')
|
|
93
|
+
.description("Show the current agent's soul, or set it (its voice/personality)")
|
|
94
|
+
.option('--json', 'Output as JSON')
|
|
95
|
+
.action((text, opts) => run('Soul', async () => {
|
|
96
|
+
const guid = requireAgentGuid();
|
|
97
|
+
if (text && text.length) {
|
|
98
|
+
const content = text.join(' ');
|
|
99
|
+
const res = await put(`/account/agents/${guid}/soul`, { content });
|
|
100
|
+
printResult('Soul updated.', opts, res.data);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
const res = await get(`/account/agents/${guid}/soul`);
|
|
104
|
+
printResult(res.data.content || '(no soul set)', opts, res.data);
|
|
105
|
+
}
|
|
106
|
+
}));
|
|
107
|
+
agentCommand
|
|
108
|
+
.command('goal [text...]')
|
|
109
|
+
.description("Show the current agent's goal, or set it")
|
|
110
|
+
.option('--clear', 'Clear the goal (back to a plain assistant)')
|
|
111
|
+
.option('--json', 'Output as JSON')
|
|
112
|
+
.action((text, opts) => run('Goal', async () => {
|
|
113
|
+
const guid = requireAgentGuid();
|
|
114
|
+
if (opts.clear) {
|
|
115
|
+
const res = await put(`/account/agents/${guid}/goal`, { goal: null });
|
|
116
|
+
printResult('Goal cleared.', opts, res.data);
|
|
117
|
+
}
|
|
118
|
+
else if (text && text.length) {
|
|
119
|
+
const goal = text.join(' ');
|
|
120
|
+
const res = await put(`/account/agents/${guid}/goal`, { goal });
|
|
121
|
+
printResult('Goal updated.', opts, res.data);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
const res = await get(`/account/agents/${guid}/goal`);
|
|
125
|
+
printResult(res.data.goal || '(no goal set)', opts, res.data);
|
|
126
|
+
}
|
|
127
|
+
}));
|
|
128
|
+
const rulesCommand = agentCommand
|
|
129
|
+
.command('rules')
|
|
130
|
+
.description("Show the agent's rules playbook (manual + learned)")
|
|
131
|
+
.option('--json', 'Output as JSON')
|
|
132
|
+
.action((opts) => run('Rules', async () => {
|
|
133
|
+
const guid = requireAgentGuid();
|
|
134
|
+
const res = await get(`/account/agents/${guid}/rules`);
|
|
135
|
+
printList(res.data, opts, 'No rules yet.', r => `[${r.source}] ${r.short_guid} ${r.text}`);
|
|
136
|
+
}));
|
|
137
|
+
rulesCommand
|
|
138
|
+
.command('add <text...>')
|
|
139
|
+
.description('Add a manual rule')
|
|
140
|
+
.option('--json', 'Output as JSON')
|
|
141
|
+
.action((text, opts) => run('Add', async () => {
|
|
142
|
+
const guid = requireAgentGuid();
|
|
143
|
+
const res = await post(`/account/agents/${guid}/rules`, { text: text.join(' ') });
|
|
144
|
+
printResult(`Added rule ${res.data[0].short_guid}.`, opts, res.data[0]);
|
|
145
|
+
}));
|
|
146
|
+
rulesCommand
|
|
147
|
+
.command('rm <rule-guid>')
|
|
148
|
+
.alias('delete')
|
|
149
|
+
.description('Deactivate a rule by its guid')
|
|
150
|
+
.option('--json', 'Output as JSON')
|
|
151
|
+
.action((ruleGuid, opts) => run('Remove', async () => {
|
|
152
|
+
const guid = requireAgentGuid();
|
|
153
|
+
await del(`/account/agents/${guid}/rules/${ruleGuid}`);
|
|
154
|
+
printResult(`Removed rule ${ruleGuid}.`, opts, { removed: ruleGuid });
|
|
155
|
+
}));
|
|
156
|
+
agentCommand
|
|
157
|
+
.command('learn')
|
|
158
|
+
.description("Teach the agent from one correction (distills a durable learned rule)")
|
|
159
|
+
.requiredOption('--original <text>', 'What the agent originally produced')
|
|
160
|
+
.requiredOption('--comment <text>', "Your correction / why it was wrong")
|
|
161
|
+
.option('--json', 'Output as JSON')
|
|
162
|
+
.action((opts) => run('Learn', async () => {
|
|
163
|
+
const guid = requireAgentGuid();
|
|
164
|
+
const res = await post(`/account/agents/${guid}/learn`, { original: opts.original, comment: opts.comment });
|
|
165
|
+
const d = res.data;
|
|
166
|
+
printResult(d.saved ? `Learned: ${d.rule.text}` : `No rule saved (${d.reason || 'too idiosyncratic to generalize'}).`, opts, d);
|
|
167
|
+
}));
|
|
77
168
|
agentCommand
|
|
78
169
|
.command('rename <new-name>')
|
|
79
170
|
.description('Rename the current agent')
|
package/dist/commands/db.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Command } from 'commander';
|
|
|
2
2
|
import { get, post, sendMessage } from '../api.js';
|
|
3
3
|
import { requireConfig } from '../config.js';
|
|
4
4
|
import { error as clrError, success } from '../colors.js';
|
|
5
|
-
import { run, printList } from '../helpers/index.js';
|
|
5
|
+
import { run, printList, emitField } from '../helpers/index.js';
|
|
6
6
|
import { confirm } from '../utils.js';
|
|
7
7
|
export const dbCommand = new Command('db')
|
|
8
8
|
.description('Manage databases');
|
|
@@ -10,6 +10,7 @@ dbCommand
|
|
|
10
10
|
.command('query <sql>')
|
|
11
11
|
.description('Run SQL')
|
|
12
12
|
.option('--database <name>', 'Database name')
|
|
13
|
+
.option('--field <path>', 'Print only this field of the result (dot path, e.g. rows.0.status)')
|
|
13
14
|
.option('--json', 'Output as JSON')
|
|
14
15
|
.action((sql, opts) => run('Query', async () => {
|
|
15
16
|
const config = requireConfig();
|
|
@@ -24,7 +25,10 @@ dbCommand
|
|
|
24
25
|
dbName = listRes.data[0].friendlyName;
|
|
25
26
|
}
|
|
26
27
|
const res = await post(`/projects/${config.projectGuid}/db/query`, { sql, database: dbName });
|
|
27
|
-
if (opts.
|
|
28
|
+
if (opts.field) {
|
|
29
|
+
emitField(res.data, opts.field);
|
|
30
|
+
}
|
|
31
|
+
else if (opts.json) {
|
|
28
32
|
console.log(JSON.stringify(res.data));
|
|
29
33
|
}
|
|
30
34
|
else {
|
package/dist/commands/deploy.js
CHANGED
|
@@ -22,7 +22,8 @@ export const deployCommand = new Command('deploy')
|
|
|
22
22
|
.option('--only <phases>', 'Run only specific phases (comma-separated)')
|
|
23
23
|
.option('--force', 'Re-run all phases (ignore checksums) and bypass the sync bulk-deletion guard')
|
|
24
24
|
.option('--no-sync', 'Skip sync-up before deploy')
|
|
25
|
-
.option('--optimize', '
|
|
25
|
+
.option('--optimize', 'Force Vite build optimization on (default for prod; use this to optimize a dev deploy too)')
|
|
26
|
+
.option('--no-optimize', 'Skip build optimization and upload files as-is - the escape hatch for plain-HTML apps whose <script src> tags are not type="module"')
|
|
26
27
|
.option('--json', 'Output as JSON')
|
|
27
28
|
.action((target, opts) => run('Deploy', async () => {
|
|
28
29
|
if (target !== 'dev' && target !== 'prod') {
|
package/dist/commands/fn.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Command } from 'commander';
|
|
|
2
2
|
import { get, post, del } from '../api.js';
|
|
3
3
|
import { requireConfig } from '../config.js';
|
|
4
4
|
import { error as clrError, bold, muted, success } from '../colors.js';
|
|
5
|
-
import { run, printList } from '../helpers/index.js';
|
|
5
|
+
import { run, printList, emitField } from '../helpers/index.js';
|
|
6
6
|
import { confirm } from '../utils.js';
|
|
7
7
|
export const fnCommand = new Command('fn')
|
|
8
8
|
.description('Manage functions');
|
|
@@ -38,12 +38,17 @@ fnCommand
|
|
|
38
38
|
.command('call <name> [body]')
|
|
39
39
|
.description('Call a function')
|
|
40
40
|
.option('--data <json>', 'JSON request body')
|
|
41
|
+
.option('--field <path>', 'Print only this field of the result (dot path, e.g. items.0.short_guid)')
|
|
41
42
|
.option('--json', 'Output as JSON')
|
|
42
43
|
.action((name, bodyArg, opts) => run('Call', async () => {
|
|
43
44
|
const config = requireConfig();
|
|
44
45
|
const raw = bodyArg || opts.data || '{}';
|
|
45
46
|
const body = JSON.parse(raw);
|
|
46
47
|
const res = await post(`/api/${config.projectGuid}/fn/${encodeURIComponent(name)}`, body);
|
|
48
|
+
if (opts.field) {
|
|
49
|
+
emitField(res.data, opts.field);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
47
52
|
console.log(opts.json ? JSON.stringify(res.data) : JSON.stringify(res.data, null, 2));
|
|
48
53
|
}));
|
|
49
54
|
fnCommand
|
|
@@ -66,6 +66,7 @@ Examples:
|
|
|
66
66
|
.option('--quality <quality>', 'Quality: low|medium|high|auto (gpt-image-2)')
|
|
67
67
|
.option('--aspect-ratio <ratio>', 'Aspect ratio (Gemini only): 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 4:5, 5:4, 21:9')
|
|
68
68
|
.option('--image-size <size>', 'Output resolution (Gemini only): 512, 1K, 2K, 4K')
|
|
69
|
+
.option('--seed <n>', 'Deterministic seed (BFL only): reuse one seed to keep a set of images visually coherent', (v) => parseInt(v, 10))
|
|
69
70
|
.option('-o, --output <file>', 'Output path (default ./generated.png). For an image your app ships, write it into the source tree so it deploys, e.g. -o src/assets/images/hero.png; the cwd default is fine for one-off generation.')
|
|
70
71
|
.option('--json', 'Output as JSON')
|
|
71
72
|
.action(async (prompt, opts) => {
|
|
@@ -79,6 +80,7 @@ Examples:
|
|
|
79
80
|
quality: opts.quality,
|
|
80
81
|
aspect_ratio: opts.aspectRatio,
|
|
81
82
|
image_size: opts.imageSize,
|
|
83
|
+
seed: Number.isFinite(opts.seed) ? opts.seed : undefined,
|
|
82
84
|
});
|
|
83
85
|
const ext = result.content_type.includes('png') ? 'png' : 'jpg';
|
|
84
86
|
const filename = opts.output || `generated.${ext}`;
|
|
@@ -90,6 +92,9 @@ Examples:
|
|
|
90
92
|
const sizeKb = Math.round(result.size_bytes / 1024);
|
|
91
93
|
console.log(`${muted(`Generated with ${result.provider}/${result.model} (${sizeKb}KB)`)}`);
|
|
92
94
|
console.log(success(`Saved to ${savedPath}`));
|
|
95
|
+
if (result.seed !== undefined) {
|
|
96
|
+
console.log(muted(`Seed ${result.seed} — pass --seed ${result.seed} to keep the next image coherent`));
|
|
97
|
+
}
|
|
93
98
|
}
|
|
94
99
|
}
|
|
95
100
|
catch (err) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { Command } from 'commander';
|
|
2
|
+
import { Command, Option } from 'commander';
|
|
3
3
|
import { post, get, ApiError } from '../api.js';
|
|
4
4
|
import { brand, bold, muted, warning } from '../colors.js';
|
|
5
5
|
import { run } from '../helpers/index.js';
|
|
@@ -122,6 +122,13 @@ export function evalExecTimeoutMessage(result) {
|
|
|
122
122
|
`and cannot extend it. Split a long interactive check into several shorter 'page eval' calls (e.g. ` +
|
|
123
123
|
`one per state to verify), keeping each body's in-page waits well under ${EVAL_EXEC_BUDGET_MS / 1000}s.`);
|
|
124
124
|
}
|
|
125
|
+
// Agents instinctively reach for a flag to pass the script (`--js`, `--script`,
|
|
126
|
+
// `--code`, …); the JS is actually the positional <expr> (or --file for a saved
|
|
127
|
+
// script). Without these, commander answers `--js` with "did you mean --json?" —
|
|
128
|
+
// a trap, since --json is a real flag that changes output but still leaves the
|
|
129
|
+
// script unset, sending the agent in a loop. Capture the common guesses as
|
|
130
|
+
// hidden decoy options so the action can redirect to the positional arg exactly.
|
|
131
|
+
const JS_DECOY_FLAGS = ['--js', '--javascript', '--script', '--code', '--expr', '--eval', '--exec'];
|
|
125
132
|
// The long-tail escape hatch alongside `page inspect`'s fixed bundle: when the
|
|
126
133
|
// curated metrics don't cover what you need (computed styles, element rects,
|
|
127
134
|
// visibility, z-index stacks), eval an expression in page context and get the
|
|
@@ -143,6 +150,13 @@ export const pageEvalCommand = new Command('eval')
|
|
|
143
150
|
.option('--wait-timeout <ms>', 'Max ms to wait for --wait-for before giving up', '5000')
|
|
144
151
|
.option('--json', 'Output as JSON')
|
|
145
152
|
.action((url, exprArg, opts) => run('Page eval', async () => {
|
|
153
|
+
// A JS-intent flag guess (captured as a hidden decoy below): redirect to the
|
|
154
|
+
// positional <expr> precisely, before the inline/--file shape checks fire.
|
|
155
|
+
const decoy = JS_DECOY_FLAGS.find((f) => opts[f.slice(2)] !== undefined);
|
|
156
|
+
if (decoy) {
|
|
157
|
+
pageEvalCommand.error(`error: ${decoy} is not a flag — pass the JavaScript as the positional <expr> argument ` +
|
|
158
|
+
`(or --file <path> for a saved script), e.g. gipity page eval "<url>" 'document.title'`);
|
|
159
|
+
}
|
|
146
160
|
// Arg-shape errors go through commander's error() so the enableHelpAfterError
|
|
147
161
|
// hook renders this command's help inline with the one-line error LAST
|
|
148
162
|
// (survives `| tail`), same as commander-detected errors like a missing url.
|
|
@@ -225,6 +239,11 @@ export const pageEvalCommand = new Command('eval')
|
|
|
225
239
|
}
|
|
226
240
|
}
|
|
227
241
|
}));
|
|
242
|
+
// Register the JS-intent flag guesses as hidden decoys (take a value so they
|
|
243
|
+
// swallow the script the agent passed) — the action turns any of them into the
|
|
244
|
+
// precise "JS is the positional arg" redirect above.
|
|
245
|
+
for (const f of JS_DECOY_FLAGS)
|
|
246
|
+
pageEvalCommand.addOption(new Option(`${f} <value>`).hideHelp());
|
|
228
247
|
// Each `page eval` call runs to completion before the next starts, so two evals
|
|
229
248
|
// fired back-to-back never coexist in time - they CANNOT test whether two live
|
|
230
249
|
// clients see each other (presence, shared state). For that, use the genuinely-
|
|
@@ -62,6 +62,40 @@ export const pageInspectCommand = new Command('inspect')
|
|
|
62
62
|
};
|
|
63
63
|
const res = await post(`/tools/browser/inspect`, inspectBody);
|
|
64
64
|
const b = res.data;
|
|
65
|
+
// ── Strip the platform's own instrumentation noise first ──
|
|
66
|
+
// Every deployed page loads Gipity's injected analytics SDK, which POSTs to
|
|
67
|
+
// Gipity's traffic/error log endpoints (`/api/<guid>/log/traffic|error`).
|
|
68
|
+
// Those are platform infrastructure, not the app's resources, so when one
|
|
69
|
+
// fails it surfaces as a failed resource on the Gipity host PLUS a generic,
|
|
70
|
+
// URL-less "Failed to load resource" console error — identical noise on
|
|
71
|
+
// essentially every deployed app. Drop both so an agent inspecting the app
|
|
72
|
+
// it just built sees only its own code's resources, not the platform's.
|
|
73
|
+
const isPlatformLog = (entry) => {
|
|
74
|
+
const urlPart = entry.replace(/\s*\([^)]*\)\s*$/, '');
|
|
75
|
+
try {
|
|
76
|
+
const u = new URL(urlPart);
|
|
77
|
+
return /(^|\.)gipity\.ai$/.test(u.hostname) && /\/log\/(traffic|error)$/.test(u.pathname);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const platformFailures = (b.failedResources || []).filter(isPlatformLog);
|
|
84
|
+
b.failedResources = (b.failedResources || []).filter((r) => !isPlatformLog(r));
|
|
85
|
+
// Each failed platform POST also emits exactly one generic, URL-less
|
|
86
|
+
// "Failed to load resource" console error. Drop one per platform failure —
|
|
87
|
+
// the text is identical, so removing by count is exact and any genuine app
|
|
88
|
+
// 404 keeps its own (indistinguishable) line.
|
|
89
|
+
let platformConsoleToDrop = platformFailures.length;
|
|
90
|
+
if (platformConsoleToDrop > 0) {
|
|
91
|
+
b.console = (b.console || []).filter((l) => {
|
|
92
|
+
if (platformConsoleToDrop > 0 && /^error:\s*Failed to load resource:/i.test(l)) {
|
|
93
|
+
platformConsoleToDrop--;
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
65
99
|
// Pull message-less cross-origin "Script error." lines out first. They carry
|
|
66
100
|
// no source/stack, so they're never actionable as app-code defects, and on a
|
|
67
101
|
// Gipity-deployed page the platform's own injected SDK is itself a
|
|
@@ -100,6 +100,7 @@ export const pageScreenshotCommand = new Command('screenshot')
|
|
|
100
100
|
// so the `?? opts.wait` merge below would never see the --wait alias. Default
|
|
101
101
|
// is applied in the merge instead.
|
|
102
102
|
.option('--post-load-delay <ms>', 'Delay after DOMContentLoaded before capture, in ms (default: 1000)')
|
|
103
|
+
.option('--action <js>', 'Run JS in the page before capturing — e.g. click a button to enter a state ("document.getElementById(\'play\').click()"). Runs after the post-load delay, then settles again before the shot.')
|
|
103
104
|
.option('--full', 'Capture the full scrollable page (default: viewport only)')
|
|
104
105
|
.option('-o, --output <file>', 'Output path (single viewport only; default .gipity/screenshots/ss-<host>-<timestamp>.png)')
|
|
105
106
|
.option('--device <names>', `Viewport preset(s): ${Object.keys(DEVICE_PRESETS).join(', ')} (comma-separated or repeat flag)`, appendOption, [])
|
|
@@ -140,6 +141,7 @@ export const pageScreenshotCommand = new Command('screenshot')
|
|
|
140
141
|
reloadBetween: opts.reloadBetween !== false,
|
|
141
142
|
...(userSpecifiedViewports ? { viewports: customViewports } : {}),
|
|
142
143
|
...(opts.fakeMedia ? { fakeMedia: true } : {}),
|
|
144
|
+
...(opts.action ? { action: opts.action } : {}),
|
|
143
145
|
};
|
|
144
146
|
const entries = await postForTarEntries('/tools/browser/screenshot', body);
|
|
145
147
|
const metaEntry = entries.find((e) => e.name === 'meta.json');
|
|
@@ -231,23 +233,26 @@ export const pageScreenshotCommand = new Command('screenshot')
|
|
|
231
233
|
console.log(`${label('Screenshot file')} ${success(savedFiles[i])}`);
|
|
232
234
|
}
|
|
233
235
|
}));
|
|
234
|
-
// `screenshot` captures the page
|
|
235
|
-
//
|
|
236
|
-
// --
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
// scroll/script/before/action. The real per-element capture lives server-side and
|
|
241
|
-
// isn't wired yet — until then, --full + crop or `page eval` cover the need.
|
|
236
|
+
// `screenshot` captures the page AFTER load + settle (+ optional --action). It does
|
|
237
|
+
// NOT scroll or wait for a selector before capture (agents reach for --scroll/
|
|
238
|
+
// --selector and get an unknown-option detour). State the supported levers right
|
|
239
|
+
// here, so the help (rendered on any bad flag, and this 'after' block survives
|
|
240
|
+
// `| tail`/`| grep`) ends the hunt in one shot. --action covers "click, then shoot";
|
|
241
|
+
// --full + crop covers off-screen regions; `page eval` reads data without a picture.
|
|
242
242
|
pageScreenshotCommand.addHelpText('after', `
|
|
243
243
|
Examples:
|
|
244
244
|
gipity page screenshot "https://dev.gipity.ai/me/app/"
|
|
245
245
|
gipity page screenshot "https://dev.gipity.ai/me/app/" --full # whole scrollable page
|
|
246
246
|
gipity page screenshot "https://dev.gipity.ai/me/app/" --device mobile,desktop
|
|
247
|
+
gipity page screenshot "https://dev.gipity.ai/me/app/" \\
|
|
248
|
+
--action "document.getElementById('play').click()" # capture an in-game frame
|
|
247
249
|
|
|
248
|
-
Capturing a
|
|
249
|
-
|
|
250
|
-
|
|
250
|
+
Capturing a state that needs an interaction (start a game, open a menu, dismiss a modal)?
|
|
251
|
+
Use --action to run JS in the page before the shot — it fires after the post-load
|
|
252
|
+
delay, then settles again so the result has painted. Do NOT hand-roll a 'page eval'
|
|
253
|
+
that returns a base64 image: the eval result is capped (~16KB) and truncates the PNG.
|
|
254
|
+
|
|
255
|
+
Capturing an off-screen region or reading element data?
|
|
251
256
|
• --full captures the ENTIRE scrollable page (then crop to the region).
|
|
252
257
|
• 'gipity page eval <url> "<expr>"' reads any (even off-screen) element's
|
|
253
258
|
data/state/rect without a picture — e.g. read the chart's bar values
|
package/dist/commands/sandbox.js
CHANGED
|
@@ -150,6 +150,17 @@ GCC/Rust).
|
|
|
150
150
|
}
|
|
151
151
|
const timeout = parseInt(opts.timeout, 10);
|
|
152
152
|
const cwd = resolveRelativeCwd();
|
|
153
|
+
// Push local working-tree changes up before executing. The sandbox mirrors
|
|
154
|
+
// the *server* (VFS), not the local cwd, so any input staged outside Claude's
|
|
155
|
+
// Write/Edit auto-push hook - a Bash `cp`/`ffmpeg`/redirect, or any external
|
|
156
|
+
// process - would otherwise be invisible to the run and the first invocation
|
|
157
|
+
// would silently miss its inputs. Syncing first makes the auto-mirror reflect
|
|
158
|
+
// local state regardless of how files got there ("no manual copy needed").
|
|
159
|
+
// Bidirectional + CAS, so it's a cheap manifest check when nothing changed.
|
|
160
|
+
// Symmetric with the post-run pull below. Skip in one-off mode (no project).
|
|
161
|
+
if (getConfigPath()) {
|
|
162
|
+
await sync({ interactive: false });
|
|
163
|
+
}
|
|
153
164
|
const res = await post(`/projects/${config.projectGuid}/sandbox/execute`, {
|
|
154
165
|
code: source,
|
|
155
166
|
language,
|
package/dist/helpers/index.js
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* CLI helpers - shared patterns across all commands.
|
|
3
3
|
*/
|
|
4
4
|
export { run } from './command.js';
|
|
5
|
-
export { printOutput, printList, printResult } from './output.js';
|
|
5
|
+
export { printOutput, printList, printResult, pluckField, emitField } from './output.js';
|
|
6
6
|
export { syncBeforeAction } from './sync.js';
|
|
7
7
|
//# sourceMappingURL=index.js.map
|
package/dist/helpers/output.js
CHANGED
|
@@ -63,6 +63,29 @@ export function printResult(text, opts, jsonData) {
|
|
|
63
63
|
}
|
|
64
64
|
console.log(text);
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Pluck a nested value out of a result by dot-path. Numeric segments index into
|
|
68
|
+
* arrays, so `items.0.short_guid` reaches the first item's guid. Returns
|
|
69
|
+
* `undefined` if any segment along the way is missing. This is what lets
|
|
70
|
+
* `--field` replace the `... | node -e "JSON.parse(...)"` extraction dance.
|
|
71
|
+
*/
|
|
72
|
+
export function pluckField(data, path) {
|
|
73
|
+
return path.split('.').reduce((acc, key) => (acc == null ? undefined : acc[key]), data);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Emit a single plucked field for `--field`. Scalars print raw (no quotes) so
|
|
77
|
+
* the output drops straight into `$(...)` / pipes; objects/arrays print as
|
|
78
|
+
* compact JSON. A missing path is an error (exit 1) so scripts fail loudly
|
|
79
|
+
* instead of silently consuming an empty string.
|
|
80
|
+
*/
|
|
81
|
+
export function emitField(data, path) {
|
|
82
|
+
const value = pluckField(data, path);
|
|
83
|
+
if (value === undefined) {
|
|
84
|
+
console.error(`Field not found: ${path}`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
console.log(typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value));
|
|
88
|
+
}
|
|
66
89
|
/**
|
|
67
90
|
* Print a list with JSON mode, empty state, and per-item formatting.
|
|
68
91
|
* Replaces the most common output pattern across all commands.
|
package/dist/knowledge.js
CHANGED
|
@@ -20,6 +20,7 @@ Templates:
|
|
|
20
20
|
- \`3d-world\` - Multiplayer world, 3D sandbox, shooter, exploration, virtual showroom (Three.js + Rapier + Colyseus)
|
|
21
21
|
- \`api\` - Backend service, webhook, data pipeline, chatbot, cron job - no frontend
|
|
22
22
|
- \`karaoke-captions\` - Forced-alignment app - karaoke captions, subtitle timing, language learning, dubbing alignment
|
|
23
|
+
- \`outreach-agent\` - AI outreach / drip-email funnel - reach a list of people with personalized, human-approved emails that auto-send on a schedule and a self-improving agent that learns from your edits
|
|
23
24
|
When unsure, default to \`web-simple\`. After adding the template, edit the generated files, then \`gipity deploy dev\`.
|
|
24
25
|
Only skip this on a build request if the user explicitly says not to.
|
|
25
26
|
|
|
@@ -36,7 +37,8 @@ Kits are reusable building blocks added to an existing app, not whole templates
|
|
|
36
37
|
- \`gipity add i18n\` - Multi-language for web apps - language picker, locale persistence, RTL, plural/translation lookup. Scaffolds src/js/strings.js and wires it up; move your copy there and read it with t('key'). Web only.
|
|
37
38
|
- \`gipity add records\` - Registry-driven records: declare objects/fields as data, get generic CRUD functions with validation, full-text search, soft delete, ACTOR provenance, and an audit event spine - every write is transactional (row + event). Field types include relations ({id,label}), currency, emails/phones/links composites. Ships backend functions + migrations. Needs a database (web-fullstack/api template).
|
|
38
39
|
- \`gipity add views\` - Generic UI over records-kit objects: sortable/filterable table with full-text search, create/edit/delete forms with type-appropriate widgets, kanban board with drag-to-update. Renders entirely from the field registry - zero per-object UI code. Requires the records kit.
|
|
39
|
-
- \`gipity add agent-api\` - Make your app agent-operable: named API keys (kit_api_keys) let agents and scripts write through the records kit's single write path with AGENT/API actor attribution - machine writes land on the same audit spine as human edits. Requires the records kit
|
|
40
|
+
- \`gipity add agent-api\` - Make your app agent-operable: named API keys (kit_api_keys) let agents and scripts write through the records kit's single write path with AGENT/API actor attribution - machine writes land on the same audit spine as human edits. Requires the records kit.
|
|
41
|
+
- \`gipity add contacts\` - Source-agnostic contact data layer for lead-gen/CRM apps: import people from LinkedIn CSV + Gmail + pasted lists, resolve duplicates into one person while keeping EVERY value from every source with provenance (multi-valued attributes, never overwrites). Exact email/URL auto-merge; fuzzy name+company goes to a human merge-review queue (reversible). Re-imports detect job changes and emit signals. User-definable tags, full-text search, and a transactional event spine. Ships backend functions + migrations. Needs a database (web-fullstack/api template).`;
|
|
40
42
|
export const SKILLS_CONTENT = `# Gipity Integration
|
|
41
43
|
|
|
42
44
|
Gipity is the cloud platform your project runs on - hosting, databases, deployment, file storage, code execution, workflows, and monitoring. Gip is the cloud agent that runs on Gipity.
|
|
@@ -45,7 +47,7 @@ Prefer the cheapest option that works - CLI and sandbox are instant and free, ap
|
|
|
45
47
|
|
|
46
48
|
1. CLI commands (fast, no agent overhead). The \`gipity\` CLI covers add, deploy, db, fn, logs, browser, sync, memory, skill, and more. All commands support \`--json\`.
|
|
47
49
|
2. Cloud sandbox via \`gipity sandbox run\` - Docker container with pre-installed tools for media (ffmpeg, ImageMagick, sox), documents (pandoc, LibreOffice), and data (pandas, matplotlib, sqlite3). Run \`gipity skill read sandbox-tools\` for the full toolkit. No network from inside the sandbox - fetch what you need before sending it in.
|
|
48
|
-
3. App services - runtime HTTP endpoints your deployed app calls directly at \`https://a.gipity.ai/api/<PROJECT_GUID>/services/*\`. Available: LLM, TTS, image, sound, music, transcribe, video, file upload, realtime, location. Load the matching skill (\`app-llm\`, \`app-tts\`, etc.) before writing service code - they have the schemas, auth pattern, and common-mistake guards. For one-off generation during development, prefer \`gipity generate <image|video|speech|music>\` or \`gipity chat\`. \`gipity generate\` saves to a generic file in the current directory by default (e.g. \`./generated.png\`)
|
|
50
|
+
3. App services - runtime HTTP endpoints your deployed app calls directly at \`https://a.gipity.ai/api/<PROJECT_GUID>/services/*\`. Available: LLM, TTS, image, sound, music, transcribe, video, file upload, realtime, location. Load the matching skill (\`app-llm\`, \`app-tts\`, etc.) before writing service code - they have the schemas, auth pattern, and common-mistake guards. For one-off generation during development, prefer \`gipity generate <image|video|speech|music>\` or \`gipity chat\`. \`gipity generate\` saves to a generic file in the current directory by default (e.g. \`./generated.png\`) - pass \`-o <path>\` to write it straight into your source tree so it deploys (e.g. \`gipity generate image "hero banner" -o src/assets/images/hero.png\`) instead of generating at cwd and moving it.
|
|
49
51
|
4. Delegate to Gip (\`gipity chat "<task>"\`) - only when the work genuinely needs agent reasoning or a tool not in the CLI, sandbox, or app services. Required for: Twitter/X search, Gmail, calendar, push notifications, video understanding, audio source isolation, cross-model second opinions, multi-step orchestration. Don't use \`gipity chat\` for anything the sandbox can do - it's slower and burns tokens.
|
|
50
52
|
|
|
51
53
|
You are the developer. Write files in this directory - the Gipity Claude Code plugin's hooks auto-sync them to Gipity. Don't run \`npm install\`, \`npm start\`, \`node\`, or \`python\` locally; there is no local runtime. Code runs in the Gipity sandbox.
|
|
@@ -78,21 +80,32 @@ The full "when to add a template" rule and the definition of done are spelled ou
|
|
|
78
80
|
|
|
79
81
|
Build loop: \`gipity add\` → edit files → \`gipity deploy dev\` → \`gipity page inspect <url>\` → fix any errors → repeat until the definition of done is met.
|
|
80
82
|
|
|
81
|
-
\`add\` writes real files to disk
|
|
83
|
+
\`add\` writes real files to disk - Read a scaffolded file before your first Write/Edit to it, or the call fails \`"File has not been read yet"\`. Don't rewrite from memory of the template.
|
|
82
84
|
|
|
83
85
|
Make your file changes and verify they landed, then run \`gipity deploy dev\` once. \`0 uploaded, N unchanged\` means nothing changed on disk - fix the files, don't re-run deploy or probe the environment.
|
|
84
86
|
|
|
85
87
|
Before telling the user the app is online, verify the source tree is consistent: no files named like \`* (conflict from *)*\`, and every package directory has its expected canonical entry file. If a conflict artifact exists, resolve it (keep one copy), re-deploy, and re-inspect before reporting done.
|
|
86
88
|
|
|
89
|
+
## Work on an existing project that isn't local yet
|
|
90
|
+
|
|
91
|
+
If you're pointed at a project that already exists on Gipity but has no local copy - e.g. the user gives a live URL \`https://dev.gipity.ai/<account>/<slug>/\` (or \`app.gipity.ai\` for prod) and you need its files to edit them - the last path segment is the project **slug**. Pull it down by adopting it into a directory named for the slug:
|
|
92
|
+
|
|
93
|
+
\`\`\`
|
|
94
|
+
mkdir -p ~/GipityProjects/<slug> && cd ~/GipityProjects/<slug> && gipity init <slug>
|
|
95
|
+
\`\`\`
|
|
96
|
+
|
|
97
|
+
\`init\` matches the existing remote project by slug, links this directory to it, and syncs its files down (you'll see \`Found existing project ...\` and \`Synced N changes\`). There's no separate \`clone\`/\`pull\` - \`init\` against a matching slug *is* the pull. After it finishes, the files are in cwd; edit and \`gipity deploy dev\` as usual. (Already linked to a different project in this dir? Switch and pull instead: \`gipity project <slug>\` then \`gipity sync\`. List your projects with \`gipity project --json\`.)
|
|
98
|
+
|
|
87
99
|
## CLI quick reference
|
|
88
100
|
|
|
89
101
|
Key commands: \`gipity add <template|kit>\`, \`gipity deploy dev\`, \`gipity sandbox run\`, \`gipity page inspect <url>\`, \`gipity page screenshot <url>\`, \`gipity db query "SQL"\`, \`gipity fn call <name>\`, \`gipity logs fn <name>\`, \`gipity skill read <name>\`.
|
|
102
|
+
Pull an existing remote project local (given its URL/slug): \`mkdir -p ~/GipityProjects/<slug> && cd ~/GipityProjects/<slug> && gipity init <slug>\` (adopts the matching project and syncs files down - this is the "clone").
|
|
90
103
|
For deterministic text questions (letter/word counts, substring occurrences, nth word/char, anagrams), use \`gipity text analyze "<text>"\` - local and instant, no sandbox or LLM needed.
|
|
91
104
|
Run \`gipity --help\` for the full list. Use \`--help\` on any command for details.
|
|
92
105
|
|
|
93
|
-
Function return shape: \`gipity fn call\`, the in-test \`ctx.fn.call\`/\`callAs\`, and the client \`Gipity.fn\` all return your function's value **unwrapped**
|
|
106
|
+
Function return shape: \`gipity fn call\`, the in-test \`ctx.fn.call\`/\`callAs\`, and the client \`Gipity.fn\` all return your function's value **unwrapped** - read/assert \`result.field\`. Only raw HTTP/\`curl\` wraps it as \`{ data: ... }\`; never write \`result.data.field\` in a test.
|
|
94
107
|
|
|
95
|
-
Tests write to your real DB: \`gipity test\` runs the test code sandboxed, but \`ctx.fn.call\`/\`callAs\` hit your actual deployed functions, which write to the same project database the app reads from
|
|
108
|
+
Tests write to your real DB: \`gipity test\` runs the test code sandboxed, but \`ctx.fn.call\`/\`callAs\` hit your actual deployed functions, which write to the same project database the app reads from - rows a test creates persist and surface on the live page. Register \`ctx.cleanup(fn)\` in any write-test to delete what it made; the harness runs every cleanup after the suite (even on failure).
|
|
96
109
|
|
|
97
110
|
## Tool output is complete and synchronous
|
|
98
111
|
|
package/dist/relay/state.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Local state for `gipity relay`.
|
|
3
3
|
*
|
|
4
|
-
* One file, `~/.gipity/relay.json
|
|
4
|
+
* One file, `$GIPITY_DIR/relay.json` (default `~/.gipity/relay.json`), mode 0600:
|
|
5
5
|
* {
|
|
6
6
|
* device: { guid, name, platform, token, paired_at },
|
|
7
7
|
* // (no allowlist - daemon materializes any of the user's projects on demand)
|
|
@@ -16,7 +16,13 @@
|
|
|
16
16
|
import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync, unlinkSync } from 'fs';
|
|
17
17
|
import { join } from 'path';
|
|
18
18
|
import { homedir } from 'os';
|
|
19
|
-
|
|
19
|
+
// GIPITY_DIR scopes the relay/device state the same way it scopes auth.json (see
|
|
20
|
+
// auth.ts). Without this, a separate auth context (e.g. GIPITY_DIR=~/.giprunner-prod
|
|
21
|
+
// logged in as ec-giprunner@914-6.com) would still read the DEFAULT ~/.gipity device —
|
|
22
|
+
// which is paired to a DIFFERENT account — and project/chat creation fails with
|
|
23
|
+
// "deviceGuid does not match a paired device". Scoping it lets each auth context pair
|
|
24
|
+
// and own its own device. Unset GIPITY_DIR → ~/.gipity, unchanged for normal users.
|
|
25
|
+
const RELAY_DIR = process.env.GIPITY_DIR || join(homedir(), '.gipity');
|
|
20
26
|
const RELAY_FILE = join(RELAY_DIR, 'relay.json');
|
|
21
27
|
const FILE_MODE = 0o600;
|
|
22
28
|
function emptyState() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gipity",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.395",
|
|
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",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"build": "tsc && chmod +x dist/index.js dist/gipcc.js dist/gipccd.js dist/updater/shim.js dist/updater/check.js",
|
|
13
13
|
"dev": "tsc --watch",
|
|
14
14
|
"test": "npm run test:smoke",
|
|
15
|
-
"test:smoke": "tsc && node --test dist/__tests__/utils.test.js dist/__tests__/config.test.js dist/__tests__/sync.test.js dist/__tests__/sync-apply.test.js dist/__tests__/sync-lock.test.js dist/__tests__/push-cas.test.js dist/__tests__/upload.test.js dist/__tests__/progress.test.js dist/__tests__/updater.test.js dist/__tests__/cli-smoke.test.js dist/__tests__/claude-noninteractive.test.js dist/__tests__/claude-trust.test.js dist/__tests__/relay-state.test.js dist/__tests__/relay-daemon.test.js dist/__tests__/relay-installers.test.js dist/__tests__/relay-bridge-abort.test.js dist/__tests__/relay-redact.test.js dist/__tests__/relay-machine-id.test.js dist/__tests__/stream-json.test.js dist/__tests__/relay-ingest-contract.test.js dist/__tests__/prompts.test.js dist/__tests__/capture-transcript.test.js dist/__tests__/flag-aliases.test.js dist/__tests__/adopt-cwd.test.js dist/__tests__/cli-cmd-agent.test.js dist/__tests__/cli-cmd-approval.test.js dist/__tests__/cli-cmd-audit.test.js dist/__tests__/cli-cmd-chat.test.js dist/__tests__/cli-cmd-credits.test.js dist/__tests__/cli-cmd-db.test.js dist/__tests__/cli-cmd-deploy.test.js dist/__tests__/cli-cmd-domain.test.js dist/__tests__/cli-cmd-email.test.js dist/__tests__/cli-cmd-file.test.js dist/__tests__/cli-cmd-fn.test.js dist/__tests__/cli-cmd-service.test.js dist/__tests__/cli-cmd-job.test.js dist/__tests__/cli-cmd-generate.test.js dist/__tests__/cli-cmd-gmail.test.js dist/__tests__/cli-cmd-info.test.js dist/__tests__/cli-cmd-init.test.js dist/__tests__/cli-cmd-location.test.js dist/__tests__/cli-cmd-text.test.js dist/__tests__/cli-cmd-login.test.js dist/__tests__/cli-cmd-logout.test.js dist/__tests__/cli-cmd-token.test.js dist/__tests__/cli-cmd-logs.test.js dist/__tests__/cli-cmd-memory.test.js dist/__tests__/cli-cmd-page.test.js dist/__tests__/cli-cmd-plan.test.js dist/__tests__/cli-cmd-project.test.js dist/__tests__/cli-cmd-rbac.test.js dist/__tests__/cli-cmd-realtime.test.js dist/__tests__/cli-cmd-records.test.js dist/__tests__/cli-cmd-relay.test.js dist/__tests__/cli-cmd-sandbox.test.js dist/__tests__/cli-cmd-add.test.js dist/__tests__/cli-cmd-remove.test.js dist/__tests__/cli-cmd-skill.test.js dist/__tests__/cli-cmd-test.test.js dist/__tests__/cli-cmd-workflow.test.js dist/__tests__/setup-skills-block.test.js dist/__tests__/setup-hooks.test.js",
|
|
15
|
+
"test:smoke": "tsc && node --test dist/__tests__/utils.test.js dist/__tests__/colors.test.js dist/__tests__/config.test.js dist/__tests__/sync.test.js dist/__tests__/sync-apply.test.js dist/__tests__/sync-lock.test.js dist/__tests__/push-cas.test.js dist/__tests__/upload.test.js dist/__tests__/progress.test.js dist/__tests__/updater.test.js dist/__tests__/cli-smoke.test.js dist/__tests__/claude-noninteractive.test.js dist/__tests__/claude-trust.test.js dist/__tests__/relay-state.test.js dist/__tests__/relay-daemon.test.js dist/__tests__/relay-installers.test.js dist/__tests__/relay-bridge-abort.test.js dist/__tests__/relay-redact.test.js dist/__tests__/relay-machine-id.test.js dist/__tests__/stream-json.test.js dist/__tests__/relay-ingest-contract.test.js dist/__tests__/prompts.test.js dist/__tests__/capture-transcript.test.js dist/__tests__/flag-aliases.test.js dist/__tests__/adopt-cwd.test.js dist/__tests__/cli-cmd-agent.test.js dist/__tests__/cli-cmd-approval.test.js dist/__tests__/cli-cmd-audit.test.js dist/__tests__/cli-cmd-chat.test.js dist/__tests__/cli-cmd-credits.test.js dist/__tests__/cli-cmd-db.test.js dist/__tests__/cli-cmd-deploy.test.js dist/__tests__/cli-cmd-domain.test.js dist/__tests__/cli-cmd-email.test.js dist/__tests__/cli-cmd-file.test.js dist/__tests__/cli-cmd-fn.test.js dist/__tests__/cli-cmd-service.test.js dist/__tests__/cli-cmd-job.test.js dist/__tests__/cli-cmd-generate.test.js dist/__tests__/cli-cmd-gmail.test.js dist/__tests__/cli-cmd-info.test.js dist/__tests__/cli-cmd-init.test.js dist/__tests__/cli-cmd-location.test.js dist/__tests__/cli-cmd-text.test.js dist/__tests__/cli-cmd-login.test.js dist/__tests__/cli-cmd-logout.test.js dist/__tests__/cli-cmd-token.test.js dist/__tests__/cli-cmd-logs.test.js dist/__tests__/cli-cmd-memory.test.js dist/__tests__/cli-cmd-page.test.js dist/__tests__/cli-cmd-plan.test.js dist/__tests__/cli-cmd-project.test.js dist/__tests__/cli-cmd-rbac.test.js dist/__tests__/cli-cmd-realtime.test.js dist/__tests__/cli-cmd-records.test.js dist/__tests__/cli-cmd-relay.test.js dist/__tests__/cli-cmd-sandbox.test.js dist/__tests__/cli-cmd-add.test.js dist/__tests__/cli-cmd-remove.test.js dist/__tests__/cli-cmd-skill.test.js dist/__tests__/cli-cmd-test.test.js dist/__tests__/cli-cmd-workflow.test.js dist/__tests__/setup-skills-block.test.js dist/__tests__/setup-hooks.test.js",
|
|
16
16
|
"test:e2e": "tsc && GIPITY_E2E=1 node --test --test-timeout=180000 dist/__tests__/cli-e2e-live.test.js dist/__tests__/cli-e2e-services-media-live.test.js dist/__tests__/cli-e2e-workflow-live.test.js dist/__tests__/cli-e2e-sandbox-live.test.js dist/__tests__/cli-e2e-page-fetch-live.test.js dist/__tests__/cli-e2e-page-test-live.test.js",
|
|
17
17
|
"test:e2e:sandbox": "tsc && GIPITY_E2E=1 node --test --test-timeout=180000 dist/__tests__/cli-e2e-sandbox-live.test.js"
|
|
18
18
|
},
|