gipity 1.0.429 → 1.1.2
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/README.md +67 -29
- package/dist/agents/claude-code.js +48 -0
- package/dist/agents/codex.js +45 -0
- package/dist/agents/grok.js +50 -0
- package/dist/agents/index.js +23 -0
- package/dist/agents/types.js +18 -0
- package/dist/api.js +14 -4
- package/dist/capture/sources/codex.js +216 -0
- package/dist/capture/sources/grok.js +178 -0
- package/dist/catalog.js +46 -0
- package/dist/client-context.js +2 -0
- package/dist/commands/add.js +9 -22
- package/dist/commands/build.js +1330 -0
- package/dist/commands/chat.js +9 -3
- package/dist/commands/claude.js +4 -13
- package/dist/commands/db.js +22 -5
- package/dist/commands/deploy.js +7 -0
- package/dist/commands/doctor.js +8 -2
- package/dist/commands/fn.js +24 -1
- package/dist/commands/init.js +9 -15
- package/dist/commands/logs.js +21 -2
- package/dist/commands/page-eval.js +25 -10
- package/dist/commands/page-inspect.js +7 -3
- package/dist/commands/page-screenshot.js +62 -23
- package/dist/commands/project.js +1 -1
- package/dist/commands/relay-install.js +1 -1
- package/dist/commands/relay.js +3 -3
- package/dist/commands/sandbox.js +62 -16
- package/dist/commands/setup.js +16 -10
- package/dist/commands/status.js +35 -7
- package/dist/commands/test.js +7 -0
- package/dist/commands/uninstall.js +25 -3
- package/dist/flag-aliases.js +1 -0
- package/dist/hooks/capture-runner.js +127 -38
- package/dist/index.js +1118 -361
- package/dist/knowledge.js +3 -3
- package/dist/prefs.js +42 -0
- package/dist/project-setup.js +2 -10
- package/dist/relay/daemon.js +124 -16
- package/dist/relay/diagnostics.js +4 -2
- package/dist/relay/onboarding.js +9 -9
- package/dist/relay/setup.js +8 -8
- package/dist/relay/state.js +1 -1
- package/dist/setup.js +258 -17
- package/package.json +4 -3
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 rl2 = createInterface({ input: process.stdin, output: process.stdout });
|
|
3947
|
-
return new Promise((
|
|
3947
|
+
return new Promise((resolve20) => {
|
|
3948
3948
|
rl2.question(question, (answer) => {
|
|
3949
3949
|
rl2.close();
|
|
3950
|
-
|
|
3950
|
+
resolve20(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((resolve20) => {
|
|
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 (k7 === "n") answer = false;
|
|
3993
3993
|
else answer = defaultYes;
|
|
3994
3994
|
console.log(answer ? "y" : "n");
|
|
3995
|
-
|
|
3995
|
+
resolve20(answer);
|
|
3996
3996
|
});
|
|
3997
3997
|
});
|
|
3998
3998
|
}
|
|
3999
3999
|
function pickOne(label2, max, defaultIdx = 1) {
|
|
4000
|
-
return new Promise((
|
|
4000
|
+
return new Promise((resolve20) => {
|
|
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 resolve20(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 resolve20(n);
|
|
4022
4022
|
}
|
|
4023
4023
|
console.log(String(defaultIdx));
|
|
4024
|
-
|
|
4024
|
+
resolve20(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(resolve20, reject) {
|
|
5374
|
+
promiseResolve = resolve20;
|
|
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((resolve20, reject) => {
|
|
5405
|
+
if (stream._duplexState & DESTROYED) return resolve20({ value: void 0, done: true });
|
|
5406
5406
|
stream.once("close", function() {
|
|
5407
5407
|
if (err) reject(err);
|
|
5408
|
-
else
|
|
5408
|
+
else resolve20({ value: void 0, done: true });
|
|
5409
5409
|
});
|
|
5410
5410
|
});
|
|
5411
5411
|
}
|
|
@@ -5449,8 +5449,8 @@ var require_streamx = __commonJS({
|
|
|
5449
5449
|
const writes = pending + (ws2._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((resolve20) => {
|
|
5453
|
+
state.drains.push({ writes, resolve: resolve20 });
|
|
5454
5454
|
});
|
|
5455
5455
|
}
|
|
5456
5456
|
write(data) {
|
|
@@ -5555,10 +5555,10 @@ var require_streamx = __commonJS({
|
|
|
5555
5555
|
cb2(null);
|
|
5556
5556
|
}
|
|
5557
5557
|
function pipelinePromise(...streams) {
|
|
5558
|
-
return new Promise((
|
|
5558
|
+
return new Promise((resolve20, reject) => {
|
|
5559
5559
|
return pipeline(...streams, (err) => {
|
|
5560
5560
|
if (err) return reject(err);
|
|
5561
|
-
|
|
5561
|
+
resolve20();
|
|
5562
5562
|
});
|
|
5563
5563
|
});
|
|
5564
5564
|
}
|
|
@@ -6213,16 +6213,16 @@ var require_extract = __commonJS({
|
|
|
6213
6213
|
entryCallback = null;
|
|
6214
6214
|
cb2(err);
|
|
6215
6215
|
}
|
|
6216
|
-
function onnext(
|
|
6216
|
+
function onnext(resolve20, reject) {
|
|
6217
6217
|
if (error2) {
|
|
6218
6218
|
return reject(error2);
|
|
6219
6219
|
}
|
|
6220
6220
|
if (entryStream) {
|
|
6221
|
-
|
|
6221
|
+
resolve20({ value: entryStream, done: false });
|
|
6222
6222
|
entryStream = null;
|
|
6223
6223
|
return;
|
|
6224
6224
|
}
|
|
6225
|
-
promiseResolve =
|
|
6225
|
+
promiseResolve = resolve20;
|
|
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((resolve20, reject) => {
|
|
6254
|
+
if (extract3.destroyed) return resolve20({ value: void 0, done: true });
|
|
6255
6255
|
extract3.once("close", function() {
|
|
6256
6256
|
if (err) reject(err);
|
|
6257
|
-
else
|
|
6257
|
+
else resolve20({ value: void 0, done: true });
|
|
6258
6258
|
});
|
|
6259
6259
|
});
|
|
6260
6260
|
}
|
|
@@ -6560,6 +6560,7 @@ function detectHarness() {
|
|
|
6560
6560
|
return { harness: "claude-code", harnessVersion: version, harnessSession: process.env.CLAUDE_CODE_SESSION_ID };
|
|
6561
6561
|
}
|
|
6562
6562
|
if (hasEnvPrefix("CODEX_")) return { harness: "codex" };
|
|
6563
|
+
if (hasEnvPrefix("GROK_")) return { harness: "grok", harnessSession: process.env.GROK_SESSION_ID };
|
|
6563
6564
|
if (process.env.CURSOR_TRACE_ID || hasEnvPrefix("CURSOR_") || (process.env.TERM_PROGRAM ?? "").toLowerCase().includes("cursor")) {
|
|
6564
6565
|
return { harness: "cursor" };
|
|
6565
6566
|
}
|
|
@@ -6614,7 +6615,8 @@ __export(api_exports, {
|
|
|
6614
6615
|
put: () => put,
|
|
6615
6616
|
putTimeoutMs: () => putTimeoutMs,
|
|
6616
6617
|
putToPresignedUrl: () => putToPresignedUrl,
|
|
6617
|
-
sendMessage: () => sendMessage
|
|
6618
|
+
sendMessage: () => sendMessage,
|
|
6619
|
+
usingEnvToken: () => usingEnvToken
|
|
6618
6620
|
});
|
|
6619
6621
|
import { Readable } from "stream";
|
|
6620
6622
|
async function fetchWithTimeout(url, init, timeoutMs, label2) {
|
|
@@ -6643,7 +6645,7 @@ async function shouldRetryAfter401(status, retried) {
|
|
|
6643
6645
|
return forceRefreshAccessToken();
|
|
6644
6646
|
}
|
|
6645
6647
|
function with401Hint(status, message) {
|
|
6646
|
-
return status === 401 && !usingEnvToken() ? `${message} \u2014 run: gipity login` : message;
|
|
6648
|
+
return status === 401 && !usingEnvToken() ? `${message} \u2014 run: gipity login (headless/CI: set GIPITY_TOKEN instead \u2014 gipity skill read agent-deploy)` : message;
|
|
6647
6649
|
}
|
|
6648
6650
|
async function getHeaders() {
|
|
6649
6651
|
return {
|
|
@@ -6705,7 +6707,7 @@ async function postForTarEntries(path5, body, retried = false) {
|
|
|
6705
6707
|
if (!res.body) throw new ApiError(500, "EMPTY_RESPONSE", "Server returned no body");
|
|
6706
6708
|
const extract3 = tar.extract();
|
|
6707
6709
|
const entries = [];
|
|
6708
|
-
const done = new Promise((
|
|
6710
|
+
const done = new Promise((resolve20, reject) => {
|
|
6709
6711
|
extract3.on("entry", (header, stream, next) => {
|
|
6710
6712
|
const chunks = [];
|
|
6711
6713
|
stream.on("data", (c) => chunks.push(c));
|
|
@@ -6716,7 +6718,7 @@ async function postForTarEntries(path5, body, retried = false) {
|
|
|
6716
6718
|
stream.on("error", reject);
|
|
6717
6719
|
stream.resume();
|
|
6718
6720
|
});
|
|
6719
|
-
extract3.on("finish", () =>
|
|
6721
|
+
extract3.on("finish", () => resolve20());
|
|
6720
6722
|
extract3.on("error", reject);
|
|
6721
6723
|
});
|
|
6722
6724
|
Readable.fromWeb(res.body).pipe(extract3);
|
|
@@ -6741,6 +6743,9 @@ async function sendMessage(message) {
|
|
|
6741
6743
|
if (res.data.conversationGuid !== config.conversationGuid) {
|
|
6742
6744
|
saveConfig({ ...config, conversationGuid: res.data.conversationGuid });
|
|
6743
6745
|
}
|
|
6746
|
+
if (res.data.costWarning && !res.data.content) {
|
|
6747
|
+
return res.data.costWarning.message;
|
|
6748
|
+
}
|
|
6744
6749
|
return res.data.content;
|
|
6745
6750
|
}
|
|
6746
6751
|
async function download(path5, retried = false) {
|
|
@@ -31858,9 +31863,9 @@ var {
|
|
|
31858
31863
|
// src/index.ts
|
|
31859
31864
|
init_config();
|
|
31860
31865
|
init_utils();
|
|
31861
|
-
import { readFileSync as
|
|
31866
|
+
import { readFileSync as readFileSync26 } from "fs";
|
|
31862
31867
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
31863
|
-
import { dirname as dirname13, resolve as
|
|
31868
|
+
import { dirname as dirname13, resolve as resolve19 } from "path";
|
|
31864
31869
|
|
|
31865
31870
|
// src/helpers/output.ts
|
|
31866
31871
|
var frameOpen = false;
|
|
@@ -31982,7 +31987,7 @@ Prefer the cheapest option that works - CLI and sandbox are instant and free, ap
|
|
|
31982
31987
|
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, push notifications (Gipity Notify - \`gipity add notify\`, send from a function with the injected \`notify()\`; see \`app-notify\`). 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|sound|music>\` or \`gipity chat\` - direct generation always bills you (the owner), regardless of any service \`billing_mode\` (that setting only governs the deployed app's runtime calls; never flip it just to create assets). \`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.
|
|
31983
31988
|
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.
|
|
31984
31989
|
|
|
31985
|
-
You are the developer. Write files in this directory -
|
|
31990
|
+
You are the developer. Write files in this directory - Gipity's editor hooks (installed by \`gipity init\` into Claude Code, Grok, and Codex) 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.
|
|
31986
31991
|
|
|
31987
31992
|
## Use first-party services before reaching outside
|
|
31988
31993
|
|
|
@@ -32018,7 +32023,7 @@ The full "when to add a template" rule and the definition of done are spelled ou
|
|
|
32018
32023
|
|
|
32019
32024
|
Build loop: \`gipity add\` \u2192 edit files \u2192 \`gipity deploy dev --inspect\` \u2192 fix any errors \u2192 repeat until the definition of done is met. \`deploy --inspect\` deploys and then runs the page-inspect report on the live URL in one command; use a standalone \`gipity page inspect <url>\` only when re-checking without deploying.
|
|
32020
32025
|
|
|
32021
|
-
\`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"
|
|
32026
|
+
\`add\` writes real files to disk - Read a scaffolded file with the Read tool before your first Write/Edit to it, or the call fails \`"File has not been read yet"\` (viewing it with \`cat\`/\`head\` in Bash does not count - the harness only tracks the Read tool). Don't rewrite from memory of the template.
|
|
32022
32027
|
|
|
32023
32028
|
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.
|
|
32024
32029
|
|
|
@@ -32074,7 +32079,7 @@ Every tool call returns its full output with that call. There is no output buffe
|
|
|
32074
32079
|
|
|
32075
32080
|
This directory is the app root - it holds \`.gipity.json\` (and \`gipity.yaml\` for backends) and is already your cwd, so run app commands here. Don't \`cd\` to a git root (\`git rev-parse --show-toplevel\`): when the app is nested in a larger repo that resolves outside the app.
|
|
32076
32081
|
|
|
32077
|
-
Write files locally -
|
|
32082
|
+
Write files locally - Gipity's editor hooks (installed into Claude Code, Grok, and Codex by \`gipity init\`) auto-push every save to Gipity and auto-pull remote changes (images, audio from \`gipity chat\`) before each turn. Use \`gipity sync\` if things get out of sync (or if the hooks aren't installed - \`gipity status --repair-hooks\` re-enables them). Deletes are safe - use \`rollback\` with a datetime to undo, or \`file_version_restore\` for individual files.
|
|
32078
32083
|
|
|
32079
32084
|
To keep local-only material (research clones, scratch data, vendored references) in the project directory without syncing or deploying it, list it in a \`.gipityignore\` at the project root - gitignore-style, one pattern per line, \`#\` comments. Ignored paths are invisible to sync in both directions; anything that already synced before being ignored stays on the server until you delete it.
|
|
32080
32085
|
|
|
@@ -32290,7 +32295,7 @@ function run(label2, action) {
|
|
|
32290
32295
|
init_api();
|
|
32291
32296
|
init_config();
|
|
32292
32297
|
init_utils();
|
|
32293
|
-
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync5, statSync, lstatSync, unlinkSync as unlinkSync3, readdirSync as
|
|
32298
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync5, statSync, lstatSync, unlinkSync as unlinkSync3, readdirSync as readdirSync3, rmdirSync, readFileSync as readFileSync6, renameSync, openSync as openSync2, closeSync as closeSync2, utimesSync, realpathSync } from "fs";
|
|
32294
32299
|
import { join as join4, relative, dirname as dirname4, extname as extname2, resolve as resolve4, sep } from "path";
|
|
32295
32300
|
import { hostname } from "os";
|
|
32296
32301
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -32354,25 +32359,25 @@ async function hashFile(path5) {
|
|
|
32354
32359
|
const hash = createHash("sha256");
|
|
32355
32360
|
let size = 0;
|
|
32356
32361
|
const stream = createReadStream(path5);
|
|
32357
|
-
await new Promise((
|
|
32362
|
+
await new Promise((resolve20, reject) => {
|
|
32358
32363
|
stream.on("data", (chunk) => {
|
|
32359
32364
|
const buf = chunk;
|
|
32360
32365
|
hash.update(buf);
|
|
32361
32366
|
size += buf.length;
|
|
32362
32367
|
});
|
|
32363
|
-
stream.on("end", () =>
|
|
32368
|
+
stream.on("end", () => resolve20());
|
|
32364
32369
|
stream.on("error", reject);
|
|
32365
32370
|
});
|
|
32366
32371
|
return { sha256: hash.digest("hex"), size };
|
|
32367
32372
|
}
|
|
32368
32373
|
function readRange(path5, start, end) {
|
|
32369
|
-
return new Promise((
|
|
32374
|
+
return new Promise((resolve20, reject) => {
|
|
32370
32375
|
const chunks = [];
|
|
32371
32376
|
const stream = createReadStream(path5, { start, end });
|
|
32372
32377
|
stream.on("data", (c) => {
|
|
32373
32378
|
chunks.push(c);
|
|
32374
32379
|
});
|
|
32375
|
-
stream.on("end", () =>
|
|
32380
|
+
stream.on("end", () => resolve20(Buffer.concat(chunks)));
|
|
32376
32381
|
stream.on("error", reject);
|
|
32377
32382
|
});
|
|
32378
32383
|
}
|
|
@@ -32536,11 +32541,14 @@ async function uploadCompleteBatch(projectGuid, items) {
|
|
|
32536
32541
|
// src/setup.ts
|
|
32537
32542
|
init_platform();
|
|
32538
32543
|
import { resolve as resolve3, join as join3, dirname as dirname3 } from "path";
|
|
32539
|
-
import { homedir as homedir3 } from "os";
|
|
32540
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readFileSync as readFileSync5 } from "fs";
|
|
32544
|
+
import { homedir as homedir3, tmpdir } from "os";
|
|
32545
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readFileSync as readFileSync5, mkdtempSync, rmSync, cpSync, readdirSync as readdirSync2 } from "fs";
|
|
32546
|
+
init_config();
|
|
32541
32547
|
var PRIMER_FILES = {
|
|
32542
32548
|
claude: "CLAUDE.md",
|
|
32543
32549
|
codex: "AGENTS.md",
|
|
32550
|
+
grok: "AGENTS.md",
|
|
32551
|
+
// Grok Build reads the AGENTS.md family (and CLAUDE.md) natively
|
|
32544
32552
|
aider: "AGENTS.md",
|
|
32545
32553
|
// shares the Codex primer; aider is pointed at it via .aider.conf.yml
|
|
32546
32554
|
gemini: "GEMINI.md",
|
|
@@ -32555,6 +32563,7 @@ var DEFAULT_SYNC_IGNORE = [
|
|
|
32555
32563
|
".gipity.json",
|
|
32556
32564
|
".gipity/",
|
|
32557
32565
|
".claude/",
|
|
32566
|
+
".codex/",
|
|
32558
32567
|
".gitignore",
|
|
32559
32568
|
AIDER_CONF_FILE,
|
|
32560
32569
|
// Home-directory junk: a project created inside a real home dir (or one that
|
|
@@ -32615,7 +32624,7 @@ var GIPITY_PLUGIN_ID = "gipity@gipity";
|
|
|
32615
32624
|
var GIPITY_MARKETPLACE_NAME = "gipity";
|
|
32616
32625
|
var GIPITY_MARKETPLACE_REPO = "GipityAI/skills";
|
|
32617
32626
|
var LEGACY_MARKETPLACE_REPO = "GipityAI/claude-plugin";
|
|
32618
|
-
var GIPITY_PLUGIN_VERSION = "0.
|
|
32627
|
+
var GIPITY_PLUGIN_VERSION = "0.7.0";
|
|
32619
32628
|
function isGipityManagedHookCommand(command) {
|
|
32620
32629
|
return (
|
|
32621
32630
|
// Capture hooks: bare absolute runner path or the fire-time launcher.
|
|
@@ -32702,8 +32711,8 @@ function userScopeInstallState() {
|
|
|
32702
32711
|
return { exists: false, current: false };
|
|
32703
32712
|
}
|
|
32704
32713
|
}
|
|
32705
|
-
function
|
|
32706
|
-
const probe = spawnSyncCommand(process.platform === "win32" ? "where" : "which", [
|
|
32714
|
+
function binaryOnPath(bin) {
|
|
32715
|
+
const probe = spawnSyncCommand(process.platform === "win32" ? "where" : "which", [bin], {
|
|
32707
32716
|
encoding: "utf-8"
|
|
32708
32717
|
});
|
|
32709
32718
|
return probe.status === 0 && !!probe.stdout?.toString().trim();
|
|
@@ -32711,7 +32720,7 @@ function claudeOnPath() {
|
|
|
32711
32720
|
function ensureGipityPluginInstalled() {
|
|
32712
32721
|
const state = userScopeInstallState();
|
|
32713
32722
|
if (state.current) return;
|
|
32714
|
-
if (!
|
|
32723
|
+
if (!binaryOnPath("claude")) return;
|
|
32715
32724
|
const claudeCmd = resolveCommand("claude");
|
|
32716
32725
|
spawnSyncCommand(claudeCmd, ["plugin", "marketplace", "update", GIPITY_MARKETPLACE_NAME], {
|
|
32717
32726
|
stdio: "ignore",
|
|
@@ -32723,6 +32732,146 @@ function ensureGipityPluginInstalled() {
|
|
|
32723
32732
|
timeout: 12e4
|
|
32724
32733
|
});
|
|
32725
32734
|
}
|
|
32735
|
+
function grokInstallState() {
|
|
32736
|
+
try {
|
|
32737
|
+
const p = join3(homedir3(), ".grok", "installed-plugins", "registry.json");
|
|
32738
|
+
const data = JSON.parse(readFileSync5(p, "utf-8"));
|
|
32739
|
+
const versions = [];
|
|
32740
|
+
for (const repo of Object.values(data?.repos ?? {})) {
|
|
32741
|
+
const v7 = repo?.plugins?.gipity?.version;
|
|
32742
|
+
if (typeof v7 === "string") versions.push(v7);
|
|
32743
|
+
}
|
|
32744
|
+
return {
|
|
32745
|
+
exists: versions.length > 0,
|
|
32746
|
+
current: versions.some((v7) => versionGte(v7, GIPITY_PLUGIN_VERSION))
|
|
32747
|
+
};
|
|
32748
|
+
} catch {
|
|
32749
|
+
return { exists: false, current: false };
|
|
32750
|
+
}
|
|
32751
|
+
}
|
|
32752
|
+
function ensureGrokPluginInstalled() {
|
|
32753
|
+
const state = grokInstallState();
|
|
32754
|
+
if (state.current) return;
|
|
32755
|
+
if (!binaryOnPath("grok")) return;
|
|
32756
|
+
const grokCmd = resolveCommand("grok");
|
|
32757
|
+
const verb = state.exists ? ["plugin", "update", "gipity"] : ["plugin", "install", GIPITY_MARKETPLACE_REPO, "--trust"];
|
|
32758
|
+
const res = spawnSyncCommand(grokCmd, verb, { stdio: "ignore", timeout: 12e4 });
|
|
32759
|
+
if (!state.exists && res.status === 0) {
|
|
32760
|
+
console.log("Installed the Gipity plugin for Grok (skills + file-sync hooks).");
|
|
32761
|
+
}
|
|
32762
|
+
}
|
|
32763
|
+
var AGENTS_SKILLS_DIR = join3(homedir3(), ".agents", "skills");
|
|
32764
|
+
var AGENT_HOOKS_DIR = join3(homedir3(), ".gipity", "agent-hooks");
|
|
32765
|
+
var AGENT_SKILLS_MANIFEST = join3(homedir3(), ".gipity", "agent-skills.json");
|
|
32766
|
+
function agentSkillsState() {
|
|
32767
|
+
try {
|
|
32768
|
+
const m = JSON.parse(readFileSync5(AGENT_SKILLS_MANIFEST, "utf-8"));
|
|
32769
|
+
return {
|
|
32770
|
+
current: typeof m?.version === "string" && versionGte(m.version, GIPITY_PLUGIN_VERSION),
|
|
32771
|
+
skills: Array.isArray(m?.skills) ? m.skills : []
|
|
32772
|
+
};
|
|
32773
|
+
} catch {
|
|
32774
|
+
return { current: false, skills: [] };
|
|
32775
|
+
}
|
|
32776
|
+
}
|
|
32777
|
+
function ensureAgentSkillsInstalled() {
|
|
32778
|
+
if (agentSkillsState().current) return;
|
|
32779
|
+
if (!binaryOnPath("git")) return;
|
|
32780
|
+
const tmp = mkdtempSync(join3(tmpdir(), "gipity-skills-"));
|
|
32781
|
+
try {
|
|
32782
|
+
const clone = spawnSyncCommand(
|
|
32783
|
+
resolveCommand("git"),
|
|
32784
|
+
["clone", "--depth", "1", `https://github.com/${GIPITY_MARKETPLACE_REPO}.git`, join3(tmp, "repo")],
|
|
32785
|
+
{ stdio: "ignore", timeout: 12e4 }
|
|
32786
|
+
);
|
|
32787
|
+
if (clone.status !== 0) return;
|
|
32788
|
+
const repo = join3(tmp, "repo");
|
|
32789
|
+
let version = GIPITY_PLUGIN_VERSION;
|
|
32790
|
+
try {
|
|
32791
|
+
const manifest = JSON.parse(readFileSync5(join3(repo, ".claude-plugin", "plugin.json"), "utf-8"));
|
|
32792
|
+
if (typeof manifest?.version === "string") version = manifest.version;
|
|
32793
|
+
} catch {
|
|
32794
|
+
}
|
|
32795
|
+
const skillsSrc = join3(repo, "skills");
|
|
32796
|
+
const names = [];
|
|
32797
|
+
for (const entry of readdirSync2(skillsSrc, { withFileTypes: true })) {
|
|
32798
|
+
if (!entry.isDirectory()) continue;
|
|
32799
|
+
if (!existsSync4(join3(skillsSrc, entry.name, "SKILL.md"))) continue;
|
|
32800
|
+
mkdirSync3(AGENTS_SKILLS_DIR, { recursive: true });
|
|
32801
|
+
cpSync(join3(skillsSrc, entry.name), join3(AGENTS_SKILLS_DIR, entry.name), {
|
|
32802
|
+
recursive: true,
|
|
32803
|
+
force: true
|
|
32804
|
+
});
|
|
32805
|
+
names.push(entry.name);
|
|
32806
|
+
}
|
|
32807
|
+
mkdirSync3(AGENT_HOOKS_DIR, { recursive: true });
|
|
32808
|
+
for (const script of readdirSync2(join3(repo, "hooks", "scripts"))) {
|
|
32809
|
+
cpSync(join3(repo, "hooks", "scripts", script), join3(AGENT_HOOKS_DIR, script), { force: true });
|
|
32810
|
+
}
|
|
32811
|
+
writeFileSync4(AGENT_SKILLS_MANIFEST, JSON.stringify({ version, skills: names }, null, 2) + "\n");
|
|
32812
|
+
console.log(`Installed ${names.length} Gipity skills for Codex (~/.agents/skills).`);
|
|
32813
|
+
} catch {
|
|
32814
|
+
} finally {
|
|
32815
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
32816
|
+
}
|
|
32817
|
+
}
|
|
32818
|
+
function applyCodexHooks(existing) {
|
|
32819
|
+
const launcher = join3(AGENT_HOOKS_DIR, "launch.sh");
|
|
32820
|
+
const cmd = (script, ...args) => [`sh "${launcher}" "${join3(AGENT_HOOKS_DIR, script)}"`, ...args].join(" ");
|
|
32821
|
+
const wanted = [
|
|
32822
|
+
{ event: "PostToolUse", matcher: "Edit|Write", command: cmd("sync-push.cjs"), timeout: 30 },
|
|
32823
|
+
{ event: "UserPromptSubmit", command: cmd("sync-pull.cjs"), timeout: 300 },
|
|
32824
|
+
// Session capture: mirror the Codex session into the Gipity web CLI.
|
|
32825
|
+
{ event: "SessionStart", command: cmd("capture.cjs", "codex", "session-start"), timeout: 30 },
|
|
32826
|
+
{ event: "PostToolUse", command: cmd("capture.cjs", "codex", "post-tool-use"), timeout: 30 },
|
|
32827
|
+
{ event: "Stop", command: cmd("capture.cjs", "codex", "stop"), timeout: 60 }
|
|
32828
|
+
];
|
|
32829
|
+
let settings = {};
|
|
32830
|
+
if (existing !== null) {
|
|
32831
|
+
try {
|
|
32832
|
+
settings = JSON.parse(existing);
|
|
32833
|
+
} catch {
|
|
32834
|
+
return null;
|
|
32835
|
+
}
|
|
32836
|
+
}
|
|
32837
|
+
const hooks = settings.hooks ?? (settings.hooks = {});
|
|
32838
|
+
let changed = false;
|
|
32839
|
+
for (const w of wanted) {
|
|
32840
|
+
const groups = Array.isArray(hooks[w.event]) ? hooks[w.event] : hooks[w.event] = [];
|
|
32841
|
+
const present = groups.some(
|
|
32842
|
+
(g) => Array.isArray(g?.hooks) && g.hooks.some(
|
|
32843
|
+
(h) => typeof h?.command === "string" && h.command === w.command
|
|
32844
|
+
)
|
|
32845
|
+
);
|
|
32846
|
+
if (present) continue;
|
|
32847
|
+
const group = { hooks: [{ type: "command", command: w.command, timeout: w.timeout }] };
|
|
32848
|
+
if (w.matcher) group.matcher = w.matcher;
|
|
32849
|
+
groups.push(group);
|
|
32850
|
+
changed = true;
|
|
32851
|
+
}
|
|
32852
|
+
return changed ? JSON.stringify(settings, null, 2) + "\n" : null;
|
|
32853
|
+
}
|
|
32854
|
+
function setupCodexHooks() {
|
|
32855
|
+
if (process.platform === "win32") return;
|
|
32856
|
+
const cwd = resolve3(process.cwd());
|
|
32857
|
+
if (cwd === resolve3(homedir3())) return;
|
|
32858
|
+
const path5 = join3(cwd, ".codex", "hooks.json");
|
|
32859
|
+
const existing = existsSync4(path5) ? readFileSync5(path5, "utf-8") : null;
|
|
32860
|
+
const next = applyCodexHooks(existing);
|
|
32861
|
+
if (next === null) return;
|
|
32862
|
+
mkdirSync3(dirname3(path5), { recursive: true });
|
|
32863
|
+
writeFileSync4(path5, next);
|
|
32864
|
+
if (existing === null) {
|
|
32865
|
+
console.log("Wrote Codex sync + session-capture hooks (.codex/hooks.json) - approve them once with /hooks inside Codex.");
|
|
32866
|
+
} else {
|
|
32867
|
+
console.log("Updated Codex hooks (.codex/hooks.json) - if Codex asks, re-approve them via /hooks.");
|
|
32868
|
+
}
|
|
32869
|
+
}
|
|
32870
|
+
function setupCodexIntegration() {
|
|
32871
|
+
if (!binaryOnPath("codex")) return;
|
|
32872
|
+
ensureAgentSkillsInstalled();
|
|
32873
|
+
setupCodexHooks();
|
|
32874
|
+
}
|
|
32726
32875
|
function setupClaudeHooks() {
|
|
32727
32876
|
ensureGipityPlugin();
|
|
32728
32877
|
const cwd = resolve3(process.cwd());
|
|
@@ -32744,14 +32893,21 @@ function setupClaudeHooks() {
|
|
|
32744
32893
|
}
|
|
32745
32894
|
var GIPITY_BLOCK_BEGIN = "<!-- BEGIN GIPITY INTEGRATION - auto-generated by gipity, do not edit this block -->";
|
|
32746
32895
|
var GIPITY_BLOCK_END = "<!-- END GIPITY INTEGRATION -->";
|
|
32747
|
-
function renderManagedBlock() {
|
|
32748
|
-
|
|
32896
|
+
function renderManagedBlock(apiBase2) {
|
|
32897
|
+
let body = [SKILLS_CONTENT, BUILD_VS_NON_BUILD_RULE, DEFINITION_OF_DONE].join("\n\n");
|
|
32898
|
+
const base = apiBase2.replace(/\/+$/, "");
|
|
32899
|
+
if (base !== DEFAULT_API_BASE) {
|
|
32900
|
+
body = body.replaceAll(DEFAULT_API_BASE, base);
|
|
32901
|
+
const note = `> **Platform instance:** this project runs against the Gipity platform at \`${base}\`, not the public \`${DEFAULT_API_BASE}\`. The project and its data exist only on that instance; every API/service URL in this document already points there - never substitute \`a.gipity.ai\`.`;
|
|
32902
|
+
const headingEnd = body.indexOf("\n");
|
|
32903
|
+
body = body.slice(0, headingEnd + 1) + "\n" + note + "\n" + body.slice(headingEnd + 1);
|
|
32904
|
+
}
|
|
32749
32905
|
return `${GIPITY_BLOCK_BEGIN}
|
|
32750
32906
|
${body}
|
|
32751
32907
|
${GIPITY_BLOCK_END}`;
|
|
32752
32908
|
}
|
|
32753
|
-
function applySkillsBlock(existing) {
|
|
32754
|
-
const block = renderManagedBlock();
|
|
32909
|
+
function applySkillsBlock(existing, apiBase2 = DEFAULT_API_BASE) {
|
|
32910
|
+
const block = renderManagedBlock(apiBase2);
|
|
32755
32911
|
if (existing === null) return block + "\n";
|
|
32756
32912
|
let next;
|
|
32757
32913
|
const beginIdx = existing.indexOf(GIPITY_BLOCK_BEGIN);
|
|
@@ -32770,7 +32926,7 @@ function writeSkillsFile(relPath, wrap) {
|
|
|
32770
32926
|
const path5 = resolve3(process.cwd(), relPath);
|
|
32771
32927
|
mkdirSync3(dirname3(path5), { recursive: true });
|
|
32772
32928
|
const existing = existsSync4(path5) ? readFileSync5(path5, "utf-8") : null;
|
|
32773
|
-
const baseNext = applySkillsBlock(existing);
|
|
32929
|
+
const baseNext = applySkillsBlock(existing, resolveApiBase());
|
|
32774
32930
|
const next = wrap && existing === null ? wrap(baseNext) : baseNext;
|
|
32775
32931
|
if (next !== existing) writeFileSync4(path5, next);
|
|
32776
32932
|
}
|
|
@@ -32834,14 +32990,22 @@ ${block}`
|
|
|
32834
32990
|
);
|
|
32835
32991
|
}
|
|
32836
32992
|
var SUPPORTED_TOOLS = [
|
|
32837
|
-
{ key: "claude", label: "Claude Code (CLAUDE.md)", setup: setupClaudeMd },
|
|
32838
|
-
{ key: "codex", label: "OpenAI Codex (AGENTS.md)", setup: setupAgentsMd },
|
|
32993
|
+
{ key: "claude", label: "Claude Code (CLAUDE.md + Gipity plugin)", setup: setupClaudeMd, integrate: setupClaudeHooks },
|
|
32994
|
+
{ key: "codex", label: "OpenAI Codex (AGENTS.md + skills + sync hooks)", setup: setupAgentsMd, integrate: setupCodexIntegration },
|
|
32995
|
+
{ key: "grok", label: "Grok Build (AGENTS.md + Gipity plugin)", setup: setupAgentsMd, integrate: ensureGrokPluginInstalled },
|
|
32839
32996
|
{ key: "aider", label: "Aider (AGENTS.md + .aider.conf.yml)", setup: setupAiderMd, optIn: true },
|
|
32840
32997
|
{ key: "gemini", label: "Gemini CLI (GEMINI.md)", setup: setupGeminiMd },
|
|
32841
32998
|
{ key: "copilot", label: "GitHub Copilot (.github/copilot-instructions.md)", setup: setupCopilotMd },
|
|
32842
32999
|
{ key: "cursor", label: "Cursor (.cursor/rules/gipity.mdc)", setup: setupCursorMd }
|
|
32843
33000
|
];
|
|
32844
33001
|
var DEFAULT_TOOLS = SUPPORTED_TOOLS.filter((t) => !t.optIn);
|
|
33002
|
+
function setupProjectTools(tools = DEFAULT_TOOLS) {
|
|
33003
|
+
for (const t of tools) {
|
|
33004
|
+
t.setup();
|
|
33005
|
+
t.integrate?.();
|
|
33006
|
+
}
|
|
33007
|
+
setupGitignore();
|
|
33008
|
+
}
|
|
32845
33009
|
function setupGitignore() {
|
|
32846
33010
|
const gitignorePath = resolve3(process.cwd(), ".gitignore");
|
|
32847
33011
|
const entries = [".gipity/", ".gipity.json", ...SCRATCH_IGNORE];
|
|
@@ -32986,7 +33150,7 @@ function walkLocal(root, ignorePatterns, baseline) {
|
|
|
32986
33150
|
function walk(dir) {
|
|
32987
33151
|
let entries;
|
|
32988
33152
|
try {
|
|
32989
|
-
entries =
|
|
33153
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
32990
33154
|
} catch {
|
|
32991
33155
|
return;
|
|
32992
33156
|
}
|
|
@@ -33065,7 +33229,7 @@ async function fetchRemote(projectGuid) {
|
|
|
33065
33229
|
function extractTarToMap(stream, idleMs, onBytes, keep) {
|
|
33066
33230
|
const extract3 = tar2.extract();
|
|
33067
33231
|
const files = /* @__PURE__ */ new Map();
|
|
33068
|
-
return new Promise((
|
|
33232
|
+
return new Promise((resolve20, reject) => {
|
|
33069
33233
|
let settled = false;
|
|
33070
33234
|
let idle;
|
|
33071
33235
|
const arm = () => {
|
|
@@ -33101,7 +33265,7 @@ function extractTarToMap(stream, idleMs, onBytes, keep) {
|
|
|
33101
33265
|
});
|
|
33102
33266
|
entryStream.resume();
|
|
33103
33267
|
});
|
|
33104
|
-
extract3.on("finish", () => done(
|
|
33268
|
+
extract3.on("finish", () => done(resolve20, files));
|
|
33105
33269
|
extract3.on("error", (e) => done(reject, e));
|
|
33106
33270
|
stream.on("error", (e) => done(reject, e));
|
|
33107
33271
|
arm();
|
|
@@ -33860,7 +34024,7 @@ async function syncInner(projectGuid, root, ignore2, opts, interactive) {
|
|
|
33860
34024
|
function cleanupEmptyDirs(root, emptiedDirs) {
|
|
33861
34025
|
const isEmpty = (dir) => {
|
|
33862
34026
|
try {
|
|
33863
|
-
return
|
|
34027
|
+
return readdirSync3(dir).length === 0;
|
|
33864
34028
|
} catch {
|
|
33865
34029
|
return false;
|
|
33866
34030
|
}
|
|
@@ -34144,7 +34308,7 @@ init_utils();
|
|
|
34144
34308
|
|
|
34145
34309
|
// src/adopt-cwd.ts
|
|
34146
34310
|
init_api();
|
|
34147
|
-
import { readdirSync as
|
|
34311
|
+
import { readdirSync as readdirSync5, statSync as statSync3 } from "fs";
|
|
34148
34312
|
import { join as join8, resolve as resolve6, sep as sep2, parse as parsePath } from "path";
|
|
34149
34313
|
import { homedir as homedir6 } from "os";
|
|
34150
34314
|
|
|
@@ -34152,7 +34316,7 @@ import { homedir as homedir6 } from "os";
|
|
|
34152
34316
|
init_config();
|
|
34153
34317
|
|
|
34154
34318
|
// src/template-vars.ts
|
|
34155
|
-
import { promises as fs, readdirSync as
|
|
34319
|
+
import { promises as fs, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
34156
34320
|
import { join as join5, relative as relative2, extname as extname3 } from "path";
|
|
34157
34321
|
var SUBSTITUTABLE_EXTS = /* @__PURE__ */ new Set([
|
|
34158
34322
|
".html",
|
|
@@ -34219,7 +34383,7 @@ function* walkTextFiles(root) {
|
|
|
34219
34383
|
const dir = stack.pop();
|
|
34220
34384
|
let entries;
|
|
34221
34385
|
try {
|
|
34222
|
-
entries =
|
|
34386
|
+
entries = readdirSync4(dir, { withFileTypes: true });
|
|
34223
34387
|
} catch {
|
|
34224
34388
|
continue;
|
|
34225
34389
|
}
|
|
@@ -34308,10 +34472,7 @@ async function finalizeLocalProject(opts) {
|
|
|
34308
34472
|
} catch (err) {
|
|
34309
34473
|
if (opts.sync === "strict") throw err;
|
|
34310
34474
|
}
|
|
34311
|
-
|
|
34312
|
-
if (tools.some((t) => t.key === "claude")) setupClaudeHooks();
|
|
34313
|
-
for (const t of tools) t.setup();
|
|
34314
|
-
setupGitignore();
|
|
34475
|
+
setupProjectTools(opts.tools ?? DEFAULT_TOOLS);
|
|
34315
34476
|
return { applied };
|
|
34316
34477
|
}
|
|
34317
34478
|
|
|
@@ -34490,7 +34651,7 @@ function scanForAdoption(cwd) {
|
|
|
34490
34651
|
if (truncated) return;
|
|
34491
34652
|
let entries;
|
|
34492
34653
|
try {
|
|
34493
|
-
entries =
|
|
34654
|
+
entries = readdirSync5(dir);
|
|
34494
34655
|
} catch {
|
|
34495
34656
|
return;
|
|
34496
34657
|
}
|
|
@@ -34526,7 +34687,7 @@ function scanForAdoption(cwd) {
|
|
|
34526
34687
|
function isLikelyEmpty(cwd) {
|
|
34527
34688
|
let entries;
|
|
34528
34689
|
try {
|
|
34529
|
-
entries =
|
|
34690
|
+
entries = readdirSync5(cwd);
|
|
34530
34691
|
} catch {
|
|
34531
34692
|
return true;
|
|
34532
34693
|
}
|
|
@@ -34564,7 +34725,7 @@ function canAdoptCwd(cwd) {
|
|
|
34564
34725
|
function countGitRepoChildren(dir) {
|
|
34565
34726
|
let entries;
|
|
34566
34727
|
try {
|
|
34567
|
-
entries =
|
|
34728
|
+
entries = readdirSync5(dir);
|
|
34568
34729
|
} catch {
|
|
34569
34730
|
return 0;
|
|
34570
34731
|
}
|
|
@@ -34676,14 +34837,15 @@ function resolveTools(forFlag) {
|
|
|
34676
34837
|
(t) => requested.includes(t.key) || !t.optIn && requested.includes("all")
|
|
34677
34838
|
);
|
|
34678
34839
|
}
|
|
34679
|
-
var initCommand = new Command("init").description("Link this directory to a project").addHelpText("after", "\nWrites CLAUDE.md/AGENTS.md primer files so your AI coding tool understands Gipity.").argument("[name]", "Project name/slug (defaults to current directory name)").option("--agent <guid>", "Agent GUID to use").option("--no-capture", "Don't record Claude Code sessions in this directory to your Gipity project (sets captureHooks: false in .gipity.json)").option(
|
|
34840
|
+
var initCommand = new Command("init").description("Link this directory to a project").addHelpText("after", "\nWrites CLAUDE.md/AGENTS.md primer files so your AI coding tool understands Gipity, and installs the Gipity skills + file-sync hooks into the agent CLIs found on this machine (Claude Code, Codex, Grok).").argument("[name]", "Project name/slug (defaults to current directory name)").option("--agent <guid>", "Agent GUID to use").option("--no-capture", "Don't record Claude Code sessions in this directory to your Gipity project (sets captureHooks: false in .gipity.json)").option(
|
|
34680
34841
|
"--for <tools>",
|
|
34681
34842
|
`Which AI tool primer files to write (comma-separated). Default: all except aider (opt-in - it also writes .aider.conf.yml). Choices: ${TOOL_KEYS.join(", ")}, all`
|
|
34682
34843
|
).addHelpText("after", `
|
|
34683
34844
|
Examples:
|
|
34684
34845
|
$ gipity init Link cwd as a new project (slug = dir name).
|
|
34685
34846
|
$ gipity init my-app Link cwd with an explicit slug.
|
|
34686
|
-
$ gipity init --for codex
|
|
34847
|
+
$ gipity init --for codex AGENTS.md + Codex skills/sync hooks only.
|
|
34848
|
+
$ gipity init --for grok AGENTS.md + the Gipity plugin in Grok only.
|
|
34687
34849
|
$ gipity init --for cursor,gemini Write only the Cursor + Gemini primers.
|
|
34688
34850
|
$ gipity init --for aider AGENTS.md + a read: entry in .aider.conf.yml
|
|
34689
34851
|
(aider auto-reads nothing, so it's opt-in).
|
|
@@ -34704,9 +34866,6 @@ Working with an existing Gipity project:
|
|
|
34704
34866
|
process.exit(1);
|
|
34705
34867
|
}
|
|
34706
34868
|
const wantsClaude = tools.some((t) => t.key === "claude");
|
|
34707
|
-
const writeAllPrimers = () => {
|
|
34708
|
-
for (const t of tools) t.setup();
|
|
34709
|
-
};
|
|
34710
34869
|
const primerSummary = tools.map((t) => t.label).join(", ");
|
|
34711
34870
|
try {
|
|
34712
34871
|
const auth = getAuth();
|
|
@@ -34718,9 +34877,7 @@ Working with an existing Gipity project:
|
|
|
34718
34877
|
if (existsSync8(resolve7(cwd, ".gipity.json"))) {
|
|
34719
34878
|
const existing = getConfig();
|
|
34720
34879
|
console.log(`Already linked to ${info(`"${existing?.projectSlug ?? ""}"`)} ${muted(`(${existing?.projectGuid ?? ""})`)}`);
|
|
34721
|
-
|
|
34722
|
-
writeAllPrimers();
|
|
34723
|
-
setupGitignore();
|
|
34880
|
+
setupProjectTools(tools);
|
|
34724
34881
|
if (existing) {
|
|
34725
34882
|
let changed = false;
|
|
34726
34883
|
const cur = existing.ignore ?? (existing.ignore = []);
|
|
@@ -34769,7 +34926,7 @@ Working with an existing Gipity project:
|
|
|
34769
34926
|
const sizeStr = scan.truncated ? `>${formatBytes(ADOPT_THRESHOLDS.REFUSE_BYTES)}` : formatBytes(scan.bytes);
|
|
34770
34927
|
const fileStr = scan.truncated ? `>${ADOPT_THRESHOLDS.REFUSE_FILES}` : `${scan.files}`;
|
|
34771
34928
|
console.error(error(`Directory has ${fileStr} files (${sizeStr}) - too large to adopt as a Gipity project.`));
|
|
34772
|
-
console.error(muted('Move into a subdirectory, or use `gipity
|
|
34929
|
+
console.error(muted('Move into a subdirectory, or use `gipity build` and pick "Create new project".'));
|
|
34773
34930
|
process.exit(1);
|
|
34774
34931
|
}
|
|
34775
34932
|
if (scan.tier === "moderate") {
|
|
@@ -34808,7 +34965,7 @@ Working with an existing Gipity project:
|
|
|
34808
34965
|
}
|
|
34809
34966
|
console.log(success(`Wrote primer files: ${primerSummary}.`));
|
|
34810
34967
|
if (wantsClaude) {
|
|
34811
|
-
console.log(success("Ready! Run
|
|
34968
|
+
console.log(success("Ready! Run your coding agent here (claude, codex, grok), or `gipity build` to launch one with a picker."));
|
|
34812
34969
|
if (opts.capture === false) {
|
|
34813
34970
|
console.log(muted("Session recording is off for this project (captureHooks: false in .gipity.json)."));
|
|
34814
34971
|
} else {
|
|
@@ -34831,6 +34988,7 @@ Working with an existing Gipity project:
|
|
|
34831
34988
|
|
|
34832
34989
|
// src/commands/status.ts
|
|
34833
34990
|
init_auth();
|
|
34991
|
+
init_api();
|
|
34834
34992
|
init_config();
|
|
34835
34993
|
init_colors();
|
|
34836
34994
|
import { existsSync as existsSync9, readFileSync as readFileSync10 } from "fs";
|
|
@@ -34853,6 +35011,16 @@ function checkGipityPlugin() {
|
|
|
34853
35011
|
const stale = missing.length === 1 && missing[0] === "install" && install.exists;
|
|
34854
35012
|
return { missing, ok: missing.length === 0, stale };
|
|
34855
35013
|
}
|
|
35014
|
+
async function probeAuth(loggedIn) {
|
|
35015
|
+
if (!loggedIn && !usingEnvToken()) return "none";
|
|
35016
|
+
if (!usingEnvToken() && sessionExpired()) return "expired";
|
|
35017
|
+
const timeout = new Promise((res) => setTimeout(() => res("unreachable"), 5e3).unref?.());
|
|
35018
|
+
const call = get("/users/me").then(
|
|
35019
|
+
() => "ok",
|
|
35020
|
+
(err) => err instanceof ApiError && err.statusCode === 401 ? "rejected" : "unreachable"
|
|
35021
|
+
);
|
|
35022
|
+
return Promise.race([call, timeout]);
|
|
35023
|
+
}
|
|
34856
35024
|
var statusCommand = new Command("status").alias("whoami").description("Show project and login status").option("--json", "Output as JSON").option("--repair-hooks", "Re-enable the Gipity Claude Code plugin (hooks) if missing or disabled").action(async (opts) => {
|
|
34857
35025
|
const config = getConfig();
|
|
34858
35026
|
const auth = getAuth();
|
|
@@ -34860,6 +35028,7 @@ var statusCommand = new Command("status").alias("whoami").description("Show proj
|
|
|
34860
35028
|
void cwd;
|
|
34861
35029
|
const hookCheck = config ? checkGipityPlugin() : null;
|
|
34862
35030
|
const queueDelivered = auth && !sessionExpired() ? await flushBugQueue().catch(() => 0) : 0;
|
|
35031
|
+
const probe = await probeAuth(!!auth);
|
|
34863
35032
|
if (opts.json) {
|
|
34864
35033
|
console.log(JSON.stringify({
|
|
34865
35034
|
project: config ? {
|
|
@@ -34871,9 +35040,13 @@ var statusCommand = new Command("status").alias("whoami").description("Show proj
|
|
|
34871
35040
|
} : null,
|
|
34872
35041
|
// `valid` reflects the refresh token (the real session) - access
|
|
34873
35042
|
// tokens auto-renew, so their expiry must not read as "invalid".
|
|
34874
|
-
|
|
34875
|
-
|
|
34876
|
-
|
|
35043
|
+
// `probe` is what one live call just proved: 'rejected' means every
|
|
35044
|
+
// authenticated command will fail even though `valid` reads true.
|
|
35045
|
+
auth: auth || usingEnvToken() ? {
|
|
35046
|
+
email: auth?.email,
|
|
35047
|
+
source: usingEnvToken() ? "agent-token" : "session",
|
|
35048
|
+
valid: usingEnvToken() ? probe !== "rejected" : !sessionExpired(),
|
|
35049
|
+
probe
|
|
34877
35050
|
} : null,
|
|
34878
35051
|
plugin: hookCheck
|
|
34879
35052
|
}, null, 2));
|
|
@@ -34888,12 +35061,16 @@ var statusCommand = new Command("status").alias("whoami").description("Show proj
|
|
|
34888
35061
|
console.log(`${muted("API:")} ${config.apiBase}`);
|
|
34889
35062
|
if (config.agentGuid) console.log(`${muted("Agent:")} ${config.agentGuid}`);
|
|
34890
35063
|
}
|
|
34891
|
-
if (
|
|
35064
|
+
if (usingEnvToken()) {
|
|
35065
|
+
console.log(`${muted("Auth:")} ${probe === "rejected" ? warning("agent API token (GIPITY_TOKEN) rejected by the server \u2014 mint a new one: gipity skill read agent-deploy") : success("agent API token (GIPITY_TOKEN)")}${probe === "unreachable" ? ` ${muted("(unverified \u2014 API unreachable)")}` : ""}`);
|
|
35066
|
+
} else if (!auth) {
|
|
34892
35067
|
console.log(`${muted("Auth:")} ${warning("not logged in. Run: gipity login")}`);
|
|
34893
|
-
} else if (
|
|
34894
|
-
console.log(`${muted("Auth:")} ${warning(`session expired for ${auth.email}. Run: gipity login`)}`);
|
|
35068
|
+
} else if (probe === "expired") {
|
|
35069
|
+
console.log(`${muted("Auth:")} ${warning(`session expired for ${auth.email}. Run: gipity login (headless/CI: set GIPITY_TOKEN \u2014 gipity skill read agent-deploy)`)}`);
|
|
35070
|
+
} else if (probe === "rejected") {
|
|
35071
|
+
console.log(`${muted("Auth:")} ${warning(`session for ${auth.email} was rejected by the server. Run: gipity login (headless/CI: set GIPITY_TOKEN \u2014 gipity skill read agent-deploy)`)}`);
|
|
34895
35072
|
} else {
|
|
34896
|
-
console.log(`${muted("Auth:")} ${success(auth.email)}`);
|
|
35073
|
+
console.log(`${muted("Auth:")} ${success(auth.email)}${probe === "unreachable" ? ` ${muted("(unverified \u2014 API unreachable)")}` : ""}`);
|
|
34897
35074
|
}
|
|
34898
35075
|
if (queueDelivered > 0) {
|
|
34899
35076
|
console.log(`${muted("Bug queue:")} ${success(`delivered ${queueDelivered} queued bug report${queueDelivered === 1 ? "" : "s"}`)}`);
|
|
@@ -34983,7 +35160,7 @@ var pushCommand = new Command("push").description("Push one or more files").argu
|
|
|
34983
35160
|
|
|
34984
35161
|
// src/commands/upload.ts
|
|
34985
35162
|
init_config();
|
|
34986
|
-
import { statSync as statSync4, readdirSync as
|
|
35163
|
+
import { statSync as statSync4, readdirSync as readdirSync6 } from "fs";
|
|
34987
35164
|
import { join as join10, basename as basename2, posix, resolve as resolve10, dirname as dirname6 } from "path";
|
|
34988
35165
|
init_utils();
|
|
34989
35166
|
init_colors();
|
|
@@ -34992,7 +35169,7 @@ function walkFiles(root) {
|
|
|
34992
35169
|
const stack = [root];
|
|
34993
35170
|
while (stack.length) {
|
|
34994
35171
|
const dir = stack.pop();
|
|
34995
|
-
for (const entry of
|
|
35172
|
+
for (const entry of readdirSync6(dir, { withFileTypes: true })) {
|
|
34996
35173
|
const full = join10(dir, entry.name);
|
|
34997
35174
|
if (entry.isDirectory()) stack.push(full);
|
|
34998
35175
|
else if (entry.isFile()) out.push(full);
|
|
@@ -35105,7 +35282,7 @@ init_config();
|
|
|
35105
35282
|
// src/page-fixtures.ts
|
|
35106
35283
|
init_api();
|
|
35107
35284
|
init_config();
|
|
35108
|
-
import { readFileSync as readFileSync11, statSync as statSync5, existsSync as existsSync10, readdirSync as
|
|
35285
|
+
import { readFileSync as readFileSync11, statSync as statSync5, existsSync as existsSync10, readdirSync as readdirSync7 } from "node:fs";
|
|
35109
35286
|
import { basename as basename3, resolve as resolve11, relative as relative3, join as join11 } from "node:path";
|
|
35110
35287
|
var FIND_SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", ".gipity", "dist", "build", ".next", "coverage"]);
|
|
35111
35288
|
function findByBasename(root, name, limit = 3) {
|
|
@@ -35117,7 +35294,7 @@ function findByBasename(root, name, limit = 3) {
|
|
|
35117
35294
|
visited++;
|
|
35118
35295
|
let entries;
|
|
35119
35296
|
try {
|
|
35120
|
-
entries =
|
|
35297
|
+
entries = readdirSync7(dir, { withFileTypes: true });
|
|
35121
35298
|
} catch {
|
|
35122
35299
|
continue;
|
|
35123
35300
|
}
|
|
@@ -35190,7 +35367,7 @@ async function uploadCameraFeed(projectGuid, localPath) {
|
|
|
35190
35367
|
}
|
|
35191
35368
|
|
|
35192
35369
|
// src/commands/page-eval.ts
|
|
35193
|
-
var EVAL_NO_VALUE_HINT = "The eval ran but returned no JSON-serializable value. A
|
|
35370
|
+
var EVAL_NO_VALUE_HINT = "The eval ran but returned no JSON-serializable value. A body ending in a loop/if-block, a `return` of undefined, or a DOM node/function all serialize to null. End the script with an expression \u2014 or an explicit `return` \u2014 that yields plain data, e.g. `return { label: input.value, count: items.length }` or `return JSON.stringify(payload)`.";
|
|
35194
35371
|
function normalizeEvalResult(raw) {
|
|
35195
35372
|
const trimmed = (raw ?? "").trim();
|
|
35196
35373
|
if (trimmed === "" || trimmed === "null" || trimmed === "undefined") {
|
|
@@ -35310,7 +35487,7 @@ function slowRenderMessage(fps, o) {
|
|
|
35310
35487
|
const frames = Math.max(1, Math.round(fps * (o.waitMs / 1e3)));
|
|
35311
35488
|
return `${warning("\u26A0 Slow render:")} page painted at ${fps} fps, so the app's vision pipeline ran on roughly ${bold(`${frames} frame${frames === 1 ? "" : "s"}`)} during the ${Math.round(o.waitMs / 1e3)}s before this eval (it infers once per painted frame). ${bold("That is enough:")} --camera loops your still image, so every one of those frames is the SAME pixels and the model returns the SAME answer on each \u2014 re-running with a bigger --wait/--timeout cannot change the result. ${bold("Do not escalate the wait.")} If a detection landed, it is real. If nothing was detected, the suspect is the ${bold("frame")} (a model needs the whole subject in shot \u2014 a tight crop, an odd angle or a busy background reads as nothing) or the ${bold("app")} (frames never reach the model) \u2014 never the frame rate. Settle it in ONE run: ${CAMERA_FRAME_CHECK}`;
|
|
35312
35489
|
}
|
|
35313
|
-
return `${warning("\u26A0 Slow render:")} page painted at ${fps} fps. Waiting on real time (setTimeout) advances animation/physics time far slower than it looks \u2014 assertions after a wall-clock wait can report a false negative. Step the app's own loop deterministically instead (3D templates: ${bold("core.advance(seconds)")}).`;
|
|
35490
|
+
return `${warning("\u26A0 Slow render:")} page painted at ${fps} fps. Waiting on real time (setTimeout) advances animation/physics time far slower than it looks \u2014 assertions after a wall-clock wait can report a false negative. Step the app's own loop deterministically instead (3D templates: ${bold("core.advance(seconds)")}; 2D/Phaser template: ${bold("(await import('./js/config.js')).advance(seconds)")}).`;
|
|
35314
35491
|
}
|
|
35315
35492
|
async function pollEvalResult(evalJobId, expectedWorkMs) {
|
|
35316
35493
|
const deadline = Date.now() + expectedWorkMs + 6e4;
|
|
@@ -35350,8 +35527,8 @@ function evalExecTimeoutMessage(result, budgetMs) {
|
|
|
35350
35527
|
` + (budgetOverrunHint("in-page budget", budgetMs) ?? "") + `
|
|
35351
35528
|
If the slow part is a ONE-TIME page init (a WASM/model download, a big asset, a first-frame pipeline warm-up), do NOT split the body across several 'page eval' calls \u2014 every call is a fresh page load that re-pays that init, so each one hits this same wall. Have the page kick it off on load (not behind a click) and absorb it in the pre-eval window instead.`;
|
|
35352
35529
|
}
|
|
35353
|
-
var JS_DECOY_FLAGS = ["--js", "--javascript", "--script", "--code", "--expr", "--eval", "--exec"];
|
|
35354
|
-
var pageEvalCommand = new Command("eval").description("Evaluate JS in a real browser on a page (DOM, computed styles, element rects; inline expr or --file script). ONE client per call - to verify realtime/presence across concurrent clients use `page test --observe` instead").argument("<url>", "URL to load").argument("[expr]", `JavaScript to evaluate in page context (inline expression or statement body
|
|
35530
|
+
var JS_DECOY_FLAGS = ["--js", "--javascript", "--script", "--code", "--expr", "--eval", "--exec", "--action"];
|
|
35531
|
+
var pageEvalCommand = new Command("eval").description("Evaluate JS in a real browser on a page (DOM, computed styles, element rects; inline expr or --file script). ONE client per call - to verify realtime/presence across concurrent clients use `page test --observe` instead").argument("<url>", "URL to load").argument("[expr]", `JavaScript to evaluate in page context (inline expression or statement body; await works, and the trailing expression is returned automatically \u2014 REPL-style \u2014 so no explicit return is needed; result is JSON-serialized). Omit when using --file. Time budget: the body has ${EVAL_SCRIPT_BUDGET_MS / 1e3}s to finish after page load (${EVAL_SCRIPT_BUDGET_CAMERA_MS / 1e3}s with --camera) - raise it with --timeout, max ${EVAL_SCRIPT_BUDGET_MAX_MS / 1e3}s.`).option("--file <path>", `Read the script body from a file instead of the inline <expr> arg (mutually exclusive). Runs as an async function body, so top-level return/await work. Same post-load budget as <expr> (--timeout).`).option(
|
|
35355
35532
|
"--step <expr>",
|
|
35356
35533
|
`Run another expression against the SAME loaded page, after <expr> (repeat, max ${MAX_EVAL_STEPS}). Whatever that page load paid for \u2014 a vision model coming up, a game booting, a socket connecting \u2014 stays up for every step, so an N-part check costs ONE page load instead of N. Each step gets its own in-page budget and its own reported result.`,
|
|
35357
35534
|
(val, prev) => [...prev, val],
|
|
@@ -35370,7 +35547,7 @@ var pageEvalCommand = new Command("eval").description("Evaluate JS in a real bro
|
|
|
35370
35547
|
).option("--fake-media", "Grant a synthetic microphone + camera and auto-accept the getUserMedia prompt, so a voice/camera app runs headlessly instead of hitting its no-camera path. The feed is a built-in test pattern (and a tone) \u2014 nothing a vision model can recognize; to drive a vision app use --camera <path> instead.").option("--wait <ms>", "Sleep this many ms after DOMContentLoaded before evaluating (lets late async work settle; max 30000). With --wait-for, it elapses AFTER the selector appears - gate, then settle.", "500").option("--wait-for <selector>", `Wait until this CSS selector appears before evaluating, then evaluate (deterministic - beats guessing a --wait). Gate on the state you are ASSERTING, not just a ready flag: '#verdict:not(:empty)' returns the instant the round lands, where a fixed wait snapshots mid-sequence. Same flag on page inspect/screenshot. Max ${WAIT_FOR_MAX_MS}ms (--wait-timeout).`).option("--wait-timeout <ms>", `Max ms to wait for --wait-for before giving up (max ${WAIT_FOR_MAX_MS})`, "5000").option(
|
|
35371
35548
|
"--timeout <ms>",
|
|
35372
35549
|
`How long the script itself may run IN the page - its own await/setTimeout pauses count (default ${EVAL_SCRIPT_BUDGET_MS}, ${EVAL_SCRIPT_BUDGET_CAMERA_MS} with --camera/--fake-media; max ${EVAL_SCRIPT_BUDGET_MAX_MS}). Raise it to trace a sequence that unfolds over time (a game round, an animation). Distinct from --wait, which only sleeps BEFORE the script.`
|
|
35373
|
-
).option("--auth", "Evaluate signed in as you (your Gipity account), so a page behind a Sign-in-with-Gipity login is reachable. Only works for apps using Sign in with Gipity, hosted on *.gipity.ai.").option("--json", "Output as JSON").action((url, exprArg, opts) => run("Page eval", async () => {
|
|
35550
|
+
).option("--auth", "Evaluate signed in as you (your Gipity account), so a page behind a Sign-in-with-Gipity login is reachable. Only works for apps using Sign in with Gipity, hosted on *.gipity.ai. Without this flag the page loads as a genuinely anonymous, signed-out visitor \u2014 nothing carries over from earlier --auth runs.").option("--json", "Output as JSON").action((url, exprArg, opts) => run("Page eval", async () => {
|
|
35374
35551
|
const decoy = JS_DECOY_FLAGS.find((f) => opts[f.slice(2)] !== void 0);
|
|
35375
35552
|
if (decoy) {
|
|
35376
35553
|
pageEvalCommand.error(
|
|
@@ -35514,7 +35691,7 @@ ${hint}`);
|
|
|
35514
35691
|
}));
|
|
35515
35692
|
return;
|
|
35516
35693
|
}
|
|
35517
|
-
console.
|
|
35694
|
+
console.error(`${brand("Eval")} ${bold(d.url || url)}`);
|
|
35518
35695
|
if (d.navigationIncomplete) {
|
|
35519
35696
|
console.log(`${warning("\u26A0 Navigation incomplete:")} ${d.note || "page did not reach full load"}`);
|
|
35520
35697
|
}
|
|
@@ -35524,10 +35701,12 @@ ${hint}`);
|
|
|
35524
35701
|
if (d.auth?.requested) {
|
|
35525
35702
|
const who = getAuth()?.email;
|
|
35526
35703
|
console.log(d.auth.established ? `${muted("Auth:")} ${success("session established")}${who ? muted(` as ${who}`) : ""} ${muted("(what the page renders with it is app-defined)")}` : `${warning("Auth: session NOT established")}${d.auth.detail ? ` \u2014 ${d.auth.detail}` : ""} ${muted("(this is the anonymous view)")}`);
|
|
35704
|
+
} else {
|
|
35705
|
+
console.log(muted("Auth: anonymous visitor (signed out; pass --auth to run as your Gipity account)"));
|
|
35527
35706
|
}
|
|
35528
35707
|
if (camera) console.log(`${muted("Camera:")} ${camera.name} ${muted("(played as the webcam feed; getUserMedia resolves)")}`);
|
|
35529
35708
|
if (hosted.length) console.log(`${muted("Fixtures:")} ${hosted.map((h) => h.name).join(", ")}`);
|
|
35530
|
-
console.
|
|
35709
|
+
console.error(opts.file ? `${muted("Script:")} ${opts.file}` : `${muted("Expression:")} ${summarizeExpr(expr)}`);
|
|
35531
35710
|
console.log(`
|
|
35532
35711
|
${result.trim() ? result : muted("(empty result)")}`);
|
|
35533
35712
|
if (noValue) console.log(muted(`
|
|
@@ -35653,7 +35832,10 @@ function fmtMs(ms2) {
|
|
|
35653
35832
|
return ms2 >= 1e3 ? `${(ms2 / 1e3).toFixed(1)}s` : `${ms2}ms`;
|
|
35654
35833
|
}
|
|
35655
35834
|
function printAuthLine(auth) {
|
|
35656
|
-
if (!auth?.requested)
|
|
35835
|
+
if (!auth?.requested) {
|
|
35836
|
+
console.log(`${label("Auth")} ${muted("anonymous visitor (signed out; pass --auth to capture as your Gipity account)")}`);
|
|
35837
|
+
return;
|
|
35838
|
+
}
|
|
35657
35839
|
const who = getAuth()?.email;
|
|
35658
35840
|
console.log(auth.established ? `${label("Auth")} ${success("session established")}${who ? muted(` as ${who}`) : ""} ${muted("(what the page renders with it is app-defined)")}` : `${warning("Auth: session NOT established")}${auth.detail ? ` \u2014 ${auth.detail}` : ""} ${muted("(this is the anonymous view)")}`);
|
|
35659
35841
|
}
|
|
@@ -35751,7 +35933,7 @@ function splitCsv(values) {
|
|
|
35751
35933
|
function appendOption(value, previous = []) {
|
|
35752
35934
|
return [...previous, value];
|
|
35753
35935
|
}
|
|
35754
|
-
var
|
|
35936
|
+
var ACTION_ALIAS_FLAGS = ["--eval", "--js", "--javascript", "--script", "--code", "--exec"];
|
|
35755
35937
|
function buildWaitForGate(selector, timeoutMs) {
|
|
35756
35938
|
const sel = JSON.stringify(selector);
|
|
35757
35939
|
return `await (async () => { const __t0 = Date.now(); while (Date.now() - __t0 < ${timeoutMs}) { try { if (document.querySelector(${sel})) return; } catch (e) { throw new Error('wait-for: invalid selector ' + ${sel}); } await new Promise((r) => setTimeout(r, 100)); } throw new Error('wait-for: nothing matched ' + ${sel} + ' within ${timeoutMs}ms \u2014 the page never reached that state (raise --wait-timeout, fix the selector, or check the app actually gets there headlessly)'); })();`;
|
|
@@ -35760,13 +35942,14 @@ var WAIT_FOR_MAX_MS2 = 15e3;
|
|
|
35760
35942
|
var WAIT_FOR_DEFAULT_MS = 15e3;
|
|
35761
35943
|
var MAX_POST_LOAD_DELAY_MS = 3e4;
|
|
35762
35944
|
var CAMERA_DEFAULT_DELAY_MS = 15e3;
|
|
35763
|
-
var pageScreenshotCommand = new Command("screenshot").description("Screenshot a web page").argument("<url>", "URL to screenshot").option("--wait <ms>", `Sleep this many ms after DOMContentLoaded before capturing (default 1000, max ${MAX_POST_LOAD_DELAY_MS}). With --camera it defaults to ${CAMERA_DEFAULT_DELAY_MS} so the vision model is up. Same flag as page eval/inspect.`).option("--wait-for <selector>", `Wait until this CSS selector appears before capturing, then capture (deterministic - beats guessing a --wait). Same flag as page eval/inspect. Gate on what you are photographing ('[data-vision="ready"]', '#verdict:not(:empty)'). Max ${WAIT_FOR_MAX_MS2}ms (--wait-timeout).`).option("--wait-timeout <ms>", `Max ms to wait for --wait-for before giving up (default ${WAIT_FOR_DEFAULT_MS}, max ${WAIT_FOR_MAX_MS2}). Timing out is reported - the capture still happens, so you see the state it got stuck in.`).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("--
|
|
35764
|
-
const
|
|
35765
|
-
if (
|
|
35766
|
-
|
|
35767
|
-
`
|
|
35768
|
-
);
|
|
35945
|
+
var pageScreenshotCommand = new Command("screenshot").description("Screenshot a web page").argument("<url>", "URL to screenshot").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), e.g. --viewport 390x844 for a phone-sized window", appendOption, []).option("--wait <ms>", `Sleep this many ms after DOMContentLoaded before capturing (default 1000, max ${MAX_POST_LOAD_DELAY_MS}). With --camera it defaults to ${CAMERA_DEFAULT_DELAY_MS} so the vision model is up. Same flag as page eval/inspect.`).option("--wait-for <selector>", `Wait until this CSS selector appears before capturing, then capture (deterministic - beats guessing a --wait). Same flag as page eval/inspect. Gate on what you are photographing ('[data-vision="ready"]', '#verdict:not(:empty)'). Max ${WAIT_FOR_MAX_MS2}ms (--wait-timeout).`).option("--wait-timeout <ms>", `Max ms to wait for --wait-for before giving up (default ${WAIT_FOR_DEFAULT_MS}, max ${WAIT_FOR_MAX_MS2}). Timing out is reported - the capture still happens, so you see the state it got stuck in.`).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("--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. The video feed is a built-in test pattern \u2014 to capture what the app does with a REAL frame (a hand, a face, an object), use --camera <path> instead.").option("--camera <path>", "Play a local image or video (.png/.jpg/.webp/.mp4/.webm/.y4m/.mjpeg) as the browser's WEBCAM feed, then capture \u2014 so the shot shows the app reacting to a frame you chose (detected gesture, boxes, labels). Implies --fake-media.").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. Without this flag the page loads as a genuinely anonymous, signed-out visitor \u2014 nothing carries over from earlier --auth runs.").option("--ephemeral", "Skip the project screenshot history: do not persist this capture to Gipity (screenshots/ in the project). Local file is still written.").option("--json", "Output JSON metadata instead of a friendly summary").addOption(new Option("--post-load-delay <ms>", "Alias for --wait").hideHelp()).addOption(new Option("--full-page", "Alias for --full").hideHelp()).addOption(new Option("--width <px>", "Alias: --viewport WxH").hideHelp()).addOption(new Option("--height <px>", "Alias: --viewport WxH").hideHelp()).action((url, opts) => run("Page screenshot", async () => {
|
|
35946
|
+
const aliasFlag = ACTION_ALIAS_FLAGS.find((f) => opts[f.slice(2)] !== void 0);
|
|
35947
|
+
if (aliasFlag) {
|
|
35948
|
+
console.error(muted(
|
|
35949
|
+
`Note: treating ${aliasFlag} as --action \u2014 it runs your JS in the page before the capture. --action is the canonical flag.`
|
|
35950
|
+
));
|
|
35769
35951
|
}
|
|
35952
|
+
const actionScript = [opts.action, ...ACTION_ALIAS_FLAGS.map((f) => opts[f.slice(2)])].filter(Boolean).join("\n");
|
|
35770
35953
|
const delayRaw = opts.wait ?? opts.postLoadDelay;
|
|
35771
35954
|
const chosenDelay = delayRaw !== void 0;
|
|
35772
35955
|
let postLoadDelayMs = chosenDelay ? parseInt(String(delayRaw), 10) : 1e3;
|
|
@@ -35800,6 +35983,20 @@ var pageScreenshotCommand = new Command("screenshot").description("Screenshot a
|
|
|
35800
35983
|
}
|
|
35801
35984
|
const deviceNames = splitCsv(opts.device);
|
|
35802
35985
|
const viewportStrs = splitCsv(opts.viewport);
|
|
35986
|
+
if (opts.width !== void 0 || opts.height !== void 0) {
|
|
35987
|
+
if (opts.width === void 0 || opts.height === void 0) {
|
|
35988
|
+
pageScreenshotCommand.error(
|
|
35989
|
+
"error: --width and --height go together (both in px, equivalent to --viewport WxH) \u2014 or use a preset like --device mobile for a real handset (touch events, mobile user-agent, DPR)"
|
|
35990
|
+
);
|
|
35991
|
+
}
|
|
35992
|
+
if (!/^\d+$/.test(String(opts.width)) || !/^\d+$/.test(String(opts.height))) {
|
|
35993
|
+
pageScreenshotCommand.error("error: --width and --height must be integers (px)");
|
|
35994
|
+
}
|
|
35995
|
+
viewportStrs.push(`${opts.width}x${opts.height}`);
|
|
35996
|
+
if (parseInt(String(opts.width), 10) <= 500) {
|
|
35997
|
+
console.error(muted("Tip: --device mobile emulates a real handset (touch events, mobile user-agent, DPR) \u2014 a raw viewport only resizes the window."));
|
|
35998
|
+
}
|
|
35999
|
+
}
|
|
35803
36000
|
const customViewports = [
|
|
35804
36001
|
...deviceNames.map(resolveDevice),
|
|
35805
36002
|
...viewportStrs.map(parseViewportString)
|
|
@@ -35823,7 +36020,7 @@ var pageScreenshotCommand = new Command("screenshot").description("Screenshot a
|
|
|
35823
36020
|
}
|
|
35824
36021
|
const preCapture = [
|
|
35825
36022
|
opts.waitFor ? buildWaitForGate(opts.waitFor, waitForTimeoutMs) : "",
|
|
35826
|
-
|
|
36023
|
+
actionScript
|
|
35827
36024
|
].filter(Boolean).join("\n");
|
|
35828
36025
|
const body = {
|
|
35829
36026
|
url,
|
|
@@ -35945,7 +36142,7 @@ ${brand("@ " + dims)}${deviceTag}`);
|
|
|
35945
36142
|
printVfsLine(s);
|
|
35946
36143
|
}
|
|
35947
36144
|
}));
|
|
35948
|
-
for (const f of
|
|
36145
|
+
for (const f of ACTION_ALIAS_FLAGS) pageScreenshotCommand.addOption(new Option(`${f} <js>`, "Alias for --action").hideHelp());
|
|
35949
36146
|
pageScreenshotCommand.addHelpText("after", `
|
|
35950
36147
|
Examples:
|
|
35951
36148
|
gipity page screenshot "https://dev.gipity.ai/me/app/"
|
|
@@ -35992,7 +36189,7 @@ function shortUrl(url, truncate = true, maxLen = 100) {
|
|
|
35992
36189
|
const tailLen = Math.floor(keep / 2);
|
|
35993
36190
|
return result.slice(0, headLen) + "\u2026" + result.slice(-tailLen);
|
|
35994
36191
|
}
|
|
35995
|
-
var pageInspectCommand = new Command("inspect").description("Inspect a web page (console, failed resources, timing, layout overflow). ONE passive load - to verify realtime/presence across concurrent clients use `page test --observe`").argument("<url>", "URL to inspect").option("--wait <ms>", "Sleep this many ms after DOMContentLoaded before capturing (lets late async/LCP work settle; max 30000)", "500").option("--wait-for <selector>", "Wait until this CSS selector appears before capturing (deterministic; replaces --wait)").option("--wait-timeout <ms>", "Max ms to wait for --wait-for before giving up", "5000").option("--json", "Output as JSON").option("--no-reprobe", "Skip the automatic second probe that filters one-time transient console errors - roughly halves inspect time on a page with any console error, at the cost of possibly reporting a cold-load transient as real").option("--no-truncate", "Show full URLs instead of truncating long ones with middle-ellipsis").option("--all", "Include render-blocking, large resources, oversized images, overflow culprits, and LCP detail").option("--no-fake-media", "Inspect with NO camera or microphone present, so a getUserMedia app takes its no-device error path (use this only when that fallback IS what you're inspecting)").option("--device <name>", `Inspect as a real touch device: ${TOUCH_DEVICES.join(", ")}. Emulates touch events, mobile user-agent and DPR, so a touch-gated mobile layout is what gets inspected.`).option("--auth", "Load the page signed in as you (your Gipity account), so pages behind a Sign-in-with-Gipity login are reachable. Only works for apps using Sign in with Gipity, hosted on *.gipity.ai.").addOption(new Option("--screenshot [path]", "Capture a screenshot").hideHelp()).action((url, opts) => {
|
|
36192
|
+
var pageInspectCommand = new Command("inspect").description("Inspect a web page (console, failed resources, timing, layout overflow). ONE passive load - to verify realtime/presence across concurrent clients use `page test --observe`").argument("<url>", "URL to inspect").option("--wait <ms>", "Sleep this many ms after DOMContentLoaded before capturing (lets late async/LCP work settle; max 30000)", "500").option("--wait-for <selector>", "Wait until this CSS selector appears before capturing (deterministic; replaces --wait)").option("--wait-timeout <ms>", "Max ms to wait for --wait-for before giving up", "5000").option("--json", "Output as JSON").option("--no-reprobe", "Skip the automatic second probe that filters one-time transient console errors - roughly halves inspect time on a page with any console error, at the cost of possibly reporting a cold-load transient as real").option("--no-truncate", "Show full URLs instead of truncating long ones with middle-ellipsis").option("--all", "Include render-blocking, large resources, oversized images, overflow culprits, and LCP detail").option("--no-fake-media", "Inspect with NO camera or microphone present, so a getUserMedia app takes its no-device error path (use this only when that fallback IS what you're inspecting)").option("--device <name>", `Inspect as a real touch device: ${TOUCH_DEVICES.join(", ")}. Emulates touch events, mobile user-agent and DPR, so a touch-gated mobile layout is what gets inspected.`).option("--auth", "Load the page signed in as you (your Gipity account), so pages behind a Sign-in-with-Gipity login are reachable. Only works for apps using Sign in with Gipity, hosted on *.gipity.ai. Without this flag the page loads as a genuinely anonymous, signed-out visitor \u2014 nothing carries over from earlier --auth runs.").addOption(new Option("--screenshot [path]", "Capture a screenshot").hideHelp()).action((url, opts) => {
|
|
35996
36193
|
if (opts.screenshot !== void 0) {
|
|
35997
36194
|
console.error(error("page inspect does not capture screenshots. Use page screenshot:"));
|
|
35998
36195
|
console.error(` gipity page screenshot ${url}${typeof opts.screenshot === "string" ? ` -o ${opts.screenshot}` : ""}`);
|
|
@@ -36066,6 +36263,8 @@ async function inspectPage(url, opts = {}) {
|
|
|
36066
36263
|
if (b7.auth?.requested) {
|
|
36067
36264
|
const who = getAuth()?.email;
|
|
36068
36265
|
console.log(b7.auth.established ? `${muted("Auth:")} ${success("session established")}${who ? muted(` as ${who}`) : ""} ${muted("(what the page renders with it is app-defined)")}` : `${warning("Auth: session NOT established")}${b7.auth.detail ? ` \u2014 ${b7.auth.detail}` : ""} ${muted("(this is the anonymous view)")}`);
|
|
36266
|
+
} else {
|
|
36267
|
+
console.log(muted("Auth: anonymous visitor (signed out; pass --auth to load as your Gipity account)"));
|
|
36069
36268
|
}
|
|
36070
36269
|
console.log(`${muted("Title:")} ${b7.title || "(none)"}`);
|
|
36071
36270
|
console.log(`${muted("Elements:")} ${b7.elementCount || 0}`);
|
|
@@ -36255,6 +36454,10 @@ var deployCommand = new Command("deploy").description("Deploy to dev or prod").a
|
|
|
36255
36454
|
} else {
|
|
36256
36455
|
console.log(success(`\u2713 Deployed to ${target}`) + muted(` (${d.elapsedMs}ms)`));
|
|
36257
36456
|
if (d.url) console.log(`${muted("Live:")} ${brand(d.url)}`);
|
|
36457
|
+
if (d.phases?.some((p) => p.name === "functions")) {
|
|
36458
|
+
console.log(`${muted("Functions:")} POST ${brand(`${config.apiBase}/api/${config.projectGuid}/fn/<name>`)}`);
|
|
36459
|
+
console.log(muted(" (same address on dev and prod; names: gipity fn list; verify the public path: gipity fn call <name> --anon)"));
|
|
36460
|
+
}
|
|
36258
36461
|
await inspectAfter();
|
|
36259
36462
|
}
|
|
36260
36463
|
}));
|
|
@@ -36292,10 +36495,15 @@ dbCommand.command("query <sql>").description("Run SQL").option("--database <name
|
|
|
36292
36495
|
console.log(columns.map((c) => String(row2[c] ?? "NULL")).join(" "));
|
|
36293
36496
|
}
|
|
36294
36497
|
}
|
|
36498
|
+
if (res.data.affectedRows !== void 0) {
|
|
36499
|
+
console.log(`Affected rows: ${res.data.affectedRows}`);
|
|
36500
|
+
}
|
|
36295
36501
|
} else if (res.data.affectedRows !== void 0) {
|
|
36296
36502
|
console.log(`Affected rows: ${res.data.affectedRows}`);
|
|
36297
36503
|
} else if (res.data.results) {
|
|
36298
36504
|
console.log(JSON.stringify(res.data.results, null, 2));
|
|
36505
|
+
} else {
|
|
36506
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
36299
36507
|
}
|
|
36300
36508
|
}
|
|
36301
36509
|
}));
|
|
@@ -36336,11 +36544,15 @@ dbCommand.command("list").description("List databases").option("--all", "List da
|
|
|
36336
36544
|
}
|
|
36337
36545
|
}));
|
|
36338
36546
|
dbCommand.command("create <name>").description("Create a database").option("--json", "Output as JSON").action((name, opts) => run("Create", async () => {
|
|
36339
|
-
const
|
|
36547
|
+
const config = requireConfig();
|
|
36548
|
+
await post(`/projects/${config.projectGuid}/db/manage`, {
|
|
36549
|
+
action: "create",
|
|
36550
|
+
name
|
|
36551
|
+
});
|
|
36340
36552
|
if (opts.json) {
|
|
36341
|
-
console.log(JSON.stringify({
|
|
36553
|
+
console.log(JSON.stringify({ success: true, name }));
|
|
36342
36554
|
} else {
|
|
36343
|
-
console.log(
|
|
36555
|
+
console.log(success(`Created database '${name}'.`));
|
|
36344
36556
|
}
|
|
36345
36557
|
}));
|
|
36346
36558
|
dbCommand.command("drop <name>").description("Drop a database").option("--project <slug>", "Project slug (required if not in a project directory)").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action((name, opts) => run("Drop", async () => {
|
|
@@ -36428,8 +36640,8 @@ memoryCommand.command("delete <topic>").description("Delete a topic").option("--
|
|
|
36428
36640
|
// src/commands/sandbox.ts
|
|
36429
36641
|
init_api();
|
|
36430
36642
|
init_config();
|
|
36431
|
-
import { readFileSync as readFileSync13, existsSync as existsSync11, statSync as statSync6 } from "fs";
|
|
36432
|
-
import { dirname as dirname8, extname as extname4, relative as relative4, resolve as resolve12 } from "path";
|
|
36643
|
+
import { readFileSync as readFileSync13, existsSync as existsSync11, statSync as statSync6, mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
|
|
36644
|
+
import { dirname as dirname8, extname as extname4, relative as relative4, resolve as resolve12, sep as sep3 } from "path";
|
|
36433
36645
|
init_colors();
|
|
36434
36646
|
var LANG_MAP = {
|
|
36435
36647
|
js: "javascript",
|
|
@@ -36547,13 +36759,13 @@ function resolveRelativeCwd() {
|
|
|
36547
36759
|
return rel2.split(/[\\/]/).filter(Boolean).join("/");
|
|
36548
36760
|
}
|
|
36549
36761
|
var sandboxCommand = new Command("sandbox").description("Run code in a sandbox");
|
|
36550
|
-
sandboxCommand.command("run [args...]").description("Run code").option("--language <language>", "Language: js, py, or bash (required unless pinned by an interpreter token or --file extension)").option("--file <path>", "Read the code body from a file instead of the inline <code> arg; --language is inferred from the extension when not given").option("--timeout <seconds>", "Execution timeout in seconds", "30").option(
|
|
36762
|
+
sandboxCommand.command("run [args...]").description("Run code").option("--language <language>", "Language: js, py, or bash (required unless pinned by an interpreter token or --file extension)").option("--file <path>", "Read the code body from a file instead of the inline <code> arg; --language is inferred from the extension when not given").addOption(new Option("--code <code>", "Alias for the positional inline <code> arg").hideHelp()).option("--timeout <seconds>", "Execution timeout in seconds", "30").option(
|
|
36551
36763
|
"--input <path>",
|
|
36552
36764
|
"Narrow to specific project files instead of auto-mirroring the whole tree (repeatable). Use this only for >1 GB projects or when you want surgical control.",
|
|
36553
36765
|
(v7, prev) => [...prev ?? [], v7]
|
|
36554
36766
|
).option(
|
|
36555
|
-
"--
|
|
36556
|
-
|
|
36767
|
+
"--discard-output <glob>",
|
|
36768
|
+
"Discard run outputs matching this glob entirely - not saved, not returned (repeatable). To LOOK at an output without keeping it, write it under tmp/ instead: scratch outputs land on local disk only and never enter the project. Supports *, **, and dir/ prefixes.",
|
|
36557
36769
|
(v7, prev) => [...prev, v7],
|
|
36558
36770
|
[]
|
|
36559
36771
|
).option("--json", "Output as JSON").addHelpText("after", `
|
|
@@ -36561,6 +36773,11 @@ By default the whole project is auto-mirrored into /work/ (up to 1 GB) -
|
|
|
36561
36773
|
so your code can reference project files by their relative path, and any
|
|
36562
36774
|
file you write lands back in the project. No manual copy needed.
|
|
36563
36775
|
|
|
36776
|
+
Exception: tmp/ is scratch. An output written under tmp/ comes back to
|
|
36777
|
+
your local tmp/ for inspection but is never saved to the project - use it
|
|
36778
|
+
for previews and other look-once artifacts (no cleanup needed). To drop
|
|
36779
|
+
an output entirely, --discard-output <glob>.
|
|
36780
|
+
|
|
36564
36781
|
Use --input only for projects over the auto-mirror cap, or when you want
|
|
36565
36782
|
to restrict what the sandbox sees.
|
|
36566
36783
|
|
|
@@ -36577,6 +36794,11 @@ Examples:
|
|
|
36577
36794
|
$ gipity sandbox run --language python \\
|
|
36578
36795
|
"import pandas as pd; print(pd.read_csv('data/sales.csv').describe())"
|
|
36579
36796
|
|
|
36797
|
+
# Preview a generated PDF without saving the preview: write it to tmp/
|
|
36798
|
+
$ gipity sandbox run bash \\
|
|
36799
|
+
"pdftoppm -png -r 90 docs/report.pdf tmp/preview"
|
|
36800
|
+
# -> tmp/preview-1.png lands on local disk only; Read it, done.
|
|
36801
|
+
|
|
36580
36802
|
# Run a script file directly (language inferred from .py)
|
|
36581
36803
|
$ gipity sandbox run --file build_report.py
|
|
36582
36804
|
$ gipity sandbox run python build_report.py # same thing, interpreter shorthand
|
|
@@ -36598,6 +36820,13 @@ GCC/Rust).
|
|
|
36598
36820
|
let inlineCode;
|
|
36599
36821
|
let filePath = opts.file;
|
|
36600
36822
|
let langFromInterp;
|
|
36823
|
+
if (opts.code !== void 0) {
|
|
36824
|
+
if (args.length) {
|
|
36825
|
+
console.error(error("Pass the code once: either positionally or via --code, not both"));
|
|
36826
|
+
process.exit(1);
|
|
36827
|
+
}
|
|
36828
|
+
args = [opts.code];
|
|
36829
|
+
}
|
|
36601
36830
|
if (args.length >= 2 && INTERPRETERS[args[0].toLowerCase()] !== void 0) {
|
|
36602
36831
|
langFromInterp = INTERPRETERS[args[0].toLowerCase()];
|
|
36603
36832
|
const rest = args.slice(1).join(" ");
|
|
@@ -36627,9 +36856,9 @@ GCC/Rust).
|
|
|
36627
36856
|
}
|
|
36628
36857
|
}
|
|
36629
36858
|
const language = resolveLanguage({ langFromInterp, langOpt: opts.language, filePath, inlineCode });
|
|
36630
|
-
const
|
|
36631
|
-
if (
|
|
36632
|
-
console.error(error('--
|
|
36859
|
+
const discardOutput = opts.discardOutput ?? [];
|
|
36860
|
+
if (discardOutput.some((g) => !g.trim())) {
|
|
36861
|
+
console.error(error('--discard-output requires a non-empty glob (e.g. --discard-output "*.ppm")'));
|
|
36633
36862
|
process.exit(1);
|
|
36634
36863
|
}
|
|
36635
36864
|
const { config } = await resolveProjectContext();
|
|
@@ -36655,16 +36884,30 @@ GCC/Rust).
|
|
|
36655
36884
|
cwd,
|
|
36656
36885
|
// The filter must run SERVER-side (in the output extractor): skipping
|
|
36657
36886
|
// only in the CLI would still write the files into project storage and
|
|
36658
|
-
// make every later sync propose deleting them.
|
|
36659
|
-
|
|
36887
|
+
// make every later sync propose deleting them. (`noSyncOutput` is the
|
|
36888
|
+
// wire name for --discard-output.)
|
|
36889
|
+
noSyncOutput: discardOutput.length ? discardOutput : void 0
|
|
36660
36890
|
});
|
|
36661
36891
|
const res = opts.json ? await doRun() : await withSpinner("Running in sandbox\u2026", doRun, { done: null });
|
|
36662
36892
|
const pulledLocal = !!(res.data.outputFiles?.length && getConfigPath());
|
|
36663
36893
|
if (pulledLocal) {
|
|
36664
36894
|
await sync({ interactive: false, progress: opts.json ? void 0 : createProgressReporter() });
|
|
36665
36895
|
}
|
|
36896
|
+
const scratchWritten = [];
|
|
36897
|
+
if (res.data.scratchFiles?.length) {
|
|
36898
|
+
const base = resolve12(getProjectRoot() ?? process.cwd());
|
|
36899
|
+
for (const f of res.data.scratchFiles) {
|
|
36900
|
+
const rel2 = f.path.replace(/\\/g, "/");
|
|
36901
|
+
const abs = resolve12(base, rel2);
|
|
36902
|
+
if (abs !== base && !abs.startsWith(base + sep3)) continue;
|
|
36903
|
+
mkdirSync8(dirname8(abs), { recursive: true });
|
|
36904
|
+
writeFileSync9(abs, Buffer.from(f.contentBase64, "base64"));
|
|
36905
|
+
scratchWritten.push(rel2);
|
|
36906
|
+
}
|
|
36907
|
+
}
|
|
36666
36908
|
if (opts.json) {
|
|
36667
|
-
|
|
36909
|
+
const { scratchFiles: _omit, ...rest } = res.data;
|
|
36910
|
+
console.log(JSON.stringify({ ...rest, scratchFiles: scratchWritten, filesSynced: pulledLocal }));
|
|
36668
36911
|
} else {
|
|
36669
36912
|
if (res.data.autoMirrorSkipped) {
|
|
36670
36913
|
console.error(dim(`Note: ${res.data.autoMirrorSkipped.reason}`));
|
|
@@ -36694,8 +36937,12 @@ GCC/Rust).
|
|
|
36694
36937
|
if (pulledLocal) console.log(dim("Not pulled locally - run 'gipity sync' to fetch them."));
|
|
36695
36938
|
}
|
|
36696
36939
|
}
|
|
36940
|
+
if (scratchWritten.length > 0) {
|
|
36941
|
+
console.log("\nScratch outputs (local only - never synced or deployed):");
|
|
36942
|
+
for (const f of scratchWritten) console.log(`${f}`);
|
|
36943
|
+
}
|
|
36697
36944
|
if (res.data.skippedOutputFiles && res.data.skippedOutputFiles.length > 0) {
|
|
36698
|
-
console.log(dim("\
|
|
36945
|
+
console.log(dim("\nDiscarded (--discard-output):"));
|
|
36699
36946
|
for (const f of res.data.skippedOutputFiles) console.log(dim(`${f}`));
|
|
36700
36947
|
}
|
|
36701
36948
|
if (res.data.exitCode !== 0) {
|
|
@@ -36737,9 +36984,11 @@ ${syncResult.summary}`;
|
|
|
36737
36984
|
...a.remoteSize != null ? { size: a.remoteSize } : {}
|
|
36738
36985
|
}));
|
|
36739
36986
|
}
|
|
36987
|
+
const displayContent = res.data.content || res.data.costWarning?.message || "";
|
|
36740
36988
|
if (opts.json) {
|
|
36741
36989
|
console.log(JSON.stringify({
|
|
36742
|
-
content:
|
|
36990
|
+
content: displayContent,
|
|
36991
|
+
costWarning: res.data.costWarning ?? null,
|
|
36743
36992
|
toolsUsed: res.data.toolsUsed?.map((t) => ({
|
|
36744
36993
|
tool: t.toolName,
|
|
36745
36994
|
success: t.success,
|
|
@@ -36753,7 +37002,7 @@ ${syncResult.summary}`;
|
|
|
36753
37002
|
syncedFiles: syncChanges
|
|
36754
37003
|
}));
|
|
36755
37004
|
} else {
|
|
36756
|
-
console.log(
|
|
37005
|
+
console.log(displayContent);
|
|
36757
37006
|
if (res.data.toolsUsed && res.data.toolsUsed.length > 0) {
|
|
36758
37007
|
const toolNames = [...new Set(res.data.toolsUsed.map((t) => t.toolName))];
|
|
36759
37008
|
console.log(`
|
|
@@ -36799,7 +37048,7 @@ chatCommand.command("delete <guid>").description("Delete a chat").option("--json
|
|
|
36799
37048
|
init_api();
|
|
36800
37049
|
init_config();
|
|
36801
37050
|
import { join as join13 } from "path";
|
|
36802
|
-
import { mkdirSync as
|
|
37051
|
+
import { mkdirSync as mkdirSync9 } from "fs";
|
|
36803
37052
|
init_colors();
|
|
36804
37053
|
init_utils();
|
|
36805
37054
|
var projectCommand = new Command("project").description("Manage projects").argument("[name]", "Switch to project by name/slug").option("--json", "Output as JSON").action((name, opts) => run("Project", async () => {
|
|
@@ -36857,7 +37106,7 @@ projectCommand.command("create <name>").description("Create a project").option("
|
|
|
36857
37106
|
const res = await post("/projects", body);
|
|
36858
37107
|
const project = res.data;
|
|
36859
37108
|
const dir = join13(getProjectsRoot(), project.slug);
|
|
36860
|
-
|
|
37109
|
+
mkdirSync9(dir, { recursive: true });
|
|
36861
37110
|
const accountSlug = await getAccountSlug();
|
|
36862
37111
|
let agentGuid = "";
|
|
36863
37112
|
try {
|
|
@@ -36891,7 +37140,7 @@ projectCommand.command("create <name>").description("Create a project").option("
|
|
|
36891
37140
|
if (process.env.GIPITY_NON_INTERACTIVE === "1") {
|
|
36892
37141
|
console.log(`${muted("Next:")} switch to "${project.name}" in the sidebar.`);
|
|
36893
37142
|
} else {
|
|
36894
|
-
console.log(`${muted("Next:")} exit Claude (Ctrl+D), then run: ${brand("gipity
|
|
37143
|
+
console.log(`${muted("Next:")} exit Claude (Ctrl+D), then run: ${brand("gipity build")}`);
|
|
36895
37144
|
console.log(`${muted("Pick")} "${project.name}" ${muted(`- it'll be at the top of the list.`)}`);
|
|
36896
37145
|
}
|
|
36897
37146
|
}));
|
|
@@ -37112,7 +37361,7 @@ function printStepRuns(steps, emptyNote) {
|
|
|
37112
37361
|
}
|
|
37113
37362
|
}
|
|
37114
37363
|
var TERMINAL_RUN_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
37115
|
-
var sleep2 = (ms2) => new Promise((
|
|
37364
|
+
var sleep2 = (ms2) => new Promise((resolve20) => setTimeout(resolve20, ms2));
|
|
37116
37365
|
async function waitForRun(wfGuid, prevGuid, timeoutSec) {
|
|
37117
37366
|
const deadline = Date.now() + timeoutSec * 1e3;
|
|
37118
37367
|
let runGuid;
|
|
@@ -37729,14 +37978,15 @@ storageCommand.command("retention").description("View or adjust your file versio
|
|
|
37729
37978
|
printRetention(data, opts.reset === true || hasSet);
|
|
37730
37979
|
}));
|
|
37731
37980
|
|
|
37732
|
-
// src/commands/
|
|
37981
|
+
// src/commands/build.ts
|
|
37733
37982
|
init_platform();
|
|
37734
37983
|
init_auth();
|
|
37735
37984
|
init_api();
|
|
37736
|
-
import { join as
|
|
37737
|
-
import { existsSync as
|
|
37738
|
-
import { homedir as
|
|
37985
|
+
import { join as join17, dirname as dirname10, resolve as resolve14, basename as basename4, sep as sep5 } from "path";
|
|
37986
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync12, readFileSync as readFileSync17, writeFileSync as writeFileSync12, renameSync as renameSync2, readdirSync as readdirSync8, statSync as statSync7 } from "fs";
|
|
37987
|
+
import { homedir as homedir12 } from "os";
|
|
37739
37988
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
37989
|
+
import { randomUUID } from "crypto";
|
|
37740
37990
|
|
|
37741
37991
|
// src/login-flow.ts
|
|
37742
37992
|
init_api();
|
|
@@ -37775,7 +38025,7 @@ async function interactiveLogin() {
|
|
|
37775
38025
|
return getAuth();
|
|
37776
38026
|
}
|
|
37777
38027
|
|
|
37778
|
-
// src/commands/
|
|
38028
|
+
// src/commands/build.ts
|
|
37779
38029
|
init_config();
|
|
37780
38030
|
|
|
37781
38031
|
// src/prompts.ts
|
|
@@ -38053,7 +38303,7 @@ init_platform();
|
|
|
38053
38303
|
init_api();
|
|
38054
38304
|
import { hostname as hostname3, userInfo, platform as osPlatform2 } from "os";
|
|
38055
38305
|
import { resolve as resolve13, dirname as dirname9 } from "path";
|
|
38056
|
-
import { mkdirSync as
|
|
38306
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
38057
38307
|
|
|
38058
38308
|
// src/relay/machine-id.ts
|
|
38059
38309
|
import { createHash as createHash3 } from "crypto";
|
|
@@ -38128,10 +38378,6 @@ async function pairDevice(opts = {}) {
|
|
|
38128
38378
|
};
|
|
38129
38379
|
}
|
|
38130
38380
|
if (existing && opts.force) {
|
|
38131
|
-
try {
|
|
38132
|
-
await post(`/remote-devices/${encodeURIComponent(existing.guid)}/revoke`, {});
|
|
38133
|
-
} catch {
|
|
38134
|
-
}
|
|
38135
38381
|
clearDevice();
|
|
38136
38382
|
}
|
|
38137
38383
|
const name = (opts.name?.trim() || friendlyDeviceName()).trim();
|
|
@@ -38166,8 +38412,8 @@ function startDaemon() {
|
|
|
38166
38412
|
function installAutostart(opts = {}) {
|
|
38167
38413
|
const stdio = opts.stdio ?? "ignore";
|
|
38168
38414
|
const plan2 = planFor({ cliPath: resolveCliPath() });
|
|
38169
|
-
|
|
38170
|
-
|
|
38415
|
+
mkdirSync10(dirname9(plan2.path), { recursive: true });
|
|
38416
|
+
writeFileSync10(plan2.path, plan2.content);
|
|
38171
38417
|
let ok = true;
|
|
38172
38418
|
for (const argv2 of plan2.enableCmds) {
|
|
38173
38419
|
const r = spawnSyncCommand(argv2[0], argv2.slice(1), { stdio });
|
|
@@ -38223,20 +38469,20 @@ async function runRelaySetup(opts) {
|
|
|
38223
38469
|
console.log(` ${dim("To register this computer again \u2014 for example under a different name \u2014")}`);
|
|
38224
38470
|
console.log(` ${dim("unregister it first, then re-run setup:")}`);
|
|
38225
38471
|
console.log(` ${brand("gipity relay revoke")} ${dim("# unpairs this computer and removes the login service")}`);
|
|
38226
|
-
console.log(` ${brand("gipity
|
|
38472
|
+
console.log(` ${brand("gipity connect")} ${dim("# register it again (asks for a new name)")}`);
|
|
38227
38473
|
console.log("");
|
|
38228
38474
|
}
|
|
38229
38475
|
return true;
|
|
38230
38476
|
}
|
|
38231
38477
|
if (opts.mode === "run-now") {
|
|
38232
38478
|
console.log(` ${bold("Set up this computer as a relay")}`);
|
|
38233
|
-
console.log(` ${dim("A relay runs Claude Code
|
|
38234
|
-
console.log(` ${dim("It uses your Claude or
|
|
38479
|
+
console.log(` ${dim("A relay runs your coding agent (Claude Code, Codex, or Grok) here so you can drive it from the web (")}${brand("gipity.ai")}${dim(") on any browser.")}`);
|
|
38480
|
+
console.log(` ${dim("It uses your Claude, Codex, or Grok subscription \u2014 the cheapest way to pay for tokens.")}`);
|
|
38235
38481
|
} else {
|
|
38236
|
-
console.log(` ${bold("Remote control of
|
|
38237
|
-
console.log(` ${dim("Drive this
|
|
38482
|
+
console.log(` ${bold("Remote control of your coding agent")}`);
|
|
38483
|
+
console.log(` ${dim("Drive your coding agent on this computer from the web (")}${brand("gipity.ai")}${dim(") on any browser (desktop or phone).")}`);
|
|
38238
38484
|
console.log("");
|
|
38239
|
-
console.log(` ${dim("Enable now (takes 2 seconds) or turn on later with")} ${brand("gipity
|
|
38485
|
+
console.log(` ${dim("Enable now (takes 2 seconds) or turn on later with")} ${brand("gipity connect")}`);
|
|
38240
38486
|
}
|
|
38241
38487
|
console.log("");
|
|
38242
38488
|
const promptText = opts.mode === "run-now" ? " Set up remote control on this computer?" : " Enable remote control?";
|
|
@@ -38261,10 +38507,10 @@ async function runRelaySetup(opts) {
|
|
|
38261
38507
|
} catch (err) {
|
|
38262
38508
|
console.error(`
|
|
38263
38509
|
${error(`Could not create device: ${err?.message || err}`)}`);
|
|
38264
|
-
console.error(` ${dim("Skipping for now - we'll offer again next time. Or turn it on with `gipity
|
|
38510
|
+
console.error(` ${dim("Skipping for now - we'll offer again next time. Or turn it on with `gipity connect`.")}`);
|
|
38265
38511
|
return false;
|
|
38266
38512
|
}
|
|
38267
|
-
const startNow = await confirm(" Start the relay now (and on future `gipity
|
|
38513
|
+
const startNow = await confirm(" Start the relay now (and on future `gipity build` runs)?", { default: "yes" });
|
|
38268
38514
|
if (startNow) {
|
|
38269
38515
|
startDaemon();
|
|
38270
38516
|
}
|
|
@@ -38275,7 +38521,7 @@ async function runRelaySetup(opts) {
|
|
|
38275
38521
|
if (!res.ok) {
|
|
38276
38522
|
if (isWsl()) {
|
|
38277
38523
|
console.log(` ${muted("Auto-start needs systemd, which this WSL distro has off. The relay still runs -")}`);
|
|
38278
|
-
console.log(` ${muted("it started just now and `gipity
|
|
38524
|
+
console.log(` ${muted("it started just now and `gipity build` restarts it. For boot-time auto-start:")}`);
|
|
38279
38525
|
console.log(` ${muted("add [boot] systemd=true to /etc/wsl.conf, restart WSL, then run")} ${brand("gipity relay install")}${muted(".")}`);
|
|
38280
38526
|
} else {
|
|
38281
38527
|
console.log(` ${muted("Autostart install returned non-zero - you can run")} ${brand("gipity relay install")} ${muted("later.")}`);
|
|
@@ -38309,19 +38555,19 @@ async function maybeOfferRelayOn() {
|
|
|
38309
38555
|
await runRelaySetup({ mode: "offer-once" });
|
|
38310
38556
|
}
|
|
38311
38557
|
|
|
38312
|
-
// src/commands/
|
|
38558
|
+
// src/commands/build.ts
|
|
38313
38559
|
init_utils();
|
|
38314
38560
|
init_colors();
|
|
38315
38561
|
|
|
38316
38562
|
// src/banner.ts
|
|
38317
38563
|
init_colors();
|
|
38318
38564
|
import { homedir as homedir9 } from "os";
|
|
38319
|
-
import { sep as
|
|
38565
|
+
import { sep as sep4 } from "path";
|
|
38320
38566
|
function tildify(p) {
|
|
38321
38567
|
const home = homedir9();
|
|
38322
38568
|
if (!home) return p;
|
|
38323
38569
|
if (p === home) return "~";
|
|
38324
|
-
if (p.startsWith(home +
|
|
38570
|
+
if (p.startsWith(home + sep4)) return "~" + p.slice(home.length);
|
|
38325
38571
|
return p;
|
|
38326
38572
|
}
|
|
38327
38573
|
var AI_MODELS = [
|
|
@@ -38613,14 +38859,182 @@ function ensureClaudeInstalled(opts = {}) {
|
|
|
38613
38859
|
return { installed: isClaudeInstalled(), alreadyPresent: false };
|
|
38614
38860
|
}
|
|
38615
38861
|
|
|
38616
|
-
// src/
|
|
38862
|
+
// src/agents/claude-code.ts
|
|
38863
|
+
var claudeCodeAdapter = {
|
|
38864
|
+
key: "claude",
|
|
38865
|
+
source: "claude_code",
|
|
38866
|
+
displayName: "Claude Code",
|
|
38867
|
+
providerName: "Anthropic",
|
|
38868
|
+
binary: "claude",
|
|
38869
|
+
buildInteractiveArgs({ resume, model }) {
|
|
38870
|
+
const args = [];
|
|
38871
|
+
if (model) args.push("--model", model);
|
|
38872
|
+
if (resume) args.push("--resume", resume);
|
|
38873
|
+
return args;
|
|
38874
|
+
},
|
|
38875
|
+
buildHeadlessArgs({ message, resume, model, bypassApprovals, jsonStream }) {
|
|
38876
|
+
const args = ["-p", message];
|
|
38877
|
+
if (bypassApprovals) args.push("--permission-mode", "bypassPermissions");
|
|
38878
|
+
if (model) args.push("--model", model);
|
|
38879
|
+
if (resume) args.push("--resume", resume);
|
|
38880
|
+
if (jsonStream) args.push("--output-format", "stream-json", "--verbose", "--include-partial-messages");
|
|
38881
|
+
return args;
|
|
38882
|
+
},
|
|
38883
|
+
sessionIdFromStreamEvent(event) {
|
|
38884
|
+
if (event && typeof event.session_id === "string" && event.session_id) return event.session_id;
|
|
38885
|
+
return null;
|
|
38886
|
+
},
|
|
38887
|
+
hooksSupportedOnPlatform() {
|
|
38888
|
+
return true;
|
|
38889
|
+
},
|
|
38890
|
+
// The daemon parses Claude's stream-json into ingest entries itself
|
|
38891
|
+
// (hook capture stands down via GIPITY_CAPTURE=off on dispatches).
|
|
38892
|
+
daemonStreamCapture: true,
|
|
38893
|
+
ensureInstalled() {
|
|
38894
|
+
return ensureClaudeInstalled().installed;
|
|
38895
|
+
},
|
|
38896
|
+
installHint: `npm install -g ${CLAUDE_PACKAGE}`
|
|
38897
|
+
};
|
|
38898
|
+
|
|
38899
|
+
// src/agents/codex.ts
|
|
38900
|
+
var codexAdapter = {
|
|
38901
|
+
key: "codex",
|
|
38902
|
+
source: "codex",
|
|
38903
|
+
displayName: "Codex",
|
|
38904
|
+
providerName: "OpenAI",
|
|
38905
|
+
binary: "codex",
|
|
38906
|
+
buildInteractiveArgs({ resume, model }) {
|
|
38907
|
+
const args = [];
|
|
38908
|
+
if (model) args.push("--model", model);
|
|
38909
|
+
if (resume) args.push("resume", resume);
|
|
38910
|
+
return args;
|
|
38911
|
+
},
|
|
38912
|
+
buildHeadlessArgs({ message, resume, model, bypassApprovals, jsonStream }) {
|
|
38913
|
+
const args = resume ? ["exec", "resume", resume, message] : ["exec", message];
|
|
38914
|
+
if (model) args.push("--model", model);
|
|
38915
|
+
if (bypassApprovals) {
|
|
38916
|
+
args.push(
|
|
38917
|
+
"-s",
|
|
38918
|
+
"workspace-write",
|
|
38919
|
+
"-c",
|
|
38920
|
+
"sandbox_workspace_write.network_access=true",
|
|
38921
|
+
"--dangerously-bypass-hook-trust",
|
|
38922
|
+
"--skip-git-repo-check"
|
|
38923
|
+
);
|
|
38924
|
+
}
|
|
38925
|
+
if (jsonStream) args.push("--json");
|
|
38926
|
+
return args;
|
|
38927
|
+
},
|
|
38928
|
+
sessionIdFromStreamEvent(event) {
|
|
38929
|
+
if (event?.type === "thread.started" && typeof event.thread_id === "string") {
|
|
38930
|
+
return event.thread_id;
|
|
38931
|
+
}
|
|
38932
|
+
return null;
|
|
38933
|
+
},
|
|
38934
|
+
hooksSupportedOnPlatform(platform2) {
|
|
38935
|
+
return platform2 !== "win32";
|
|
38936
|
+
},
|
|
38937
|
+
// No stream-json ingest mapper for Codex: relay dispatches keep hook
|
|
38938
|
+
// capture ON (GIPITY_CONVERSATION_GUID binds it) and the transcript
|
|
38939
|
+
// parser mirrors the session; the daemon tracks byte-level progress only.
|
|
38940
|
+
daemonStreamCapture: false,
|
|
38941
|
+
oneTimeSetupNote: "Codex runs project hooks only after a one-time approval: run /hooks inside Codex and approve the Gipity entries, or session recording stays off.",
|
|
38942
|
+
installHint: "npm install -g @openai/codex"
|
|
38943
|
+
};
|
|
38944
|
+
|
|
38945
|
+
// src/agents/grok.ts
|
|
38946
|
+
var grokAdapter = {
|
|
38947
|
+
key: "grok",
|
|
38948
|
+
source: "grok",
|
|
38949
|
+
displayName: "Grok",
|
|
38950
|
+
providerName: "xAI",
|
|
38951
|
+
binary: "grok",
|
|
38952
|
+
buildInteractiveArgs({ resume, model }) {
|
|
38953
|
+
const args = [];
|
|
38954
|
+
if (model) args.push("--model", model);
|
|
38955
|
+
if (resume) args.push("--resume", resume);
|
|
38956
|
+
return args;
|
|
38957
|
+
},
|
|
38958
|
+
buildHeadlessArgs({ message, resume, model, bypassApprovals, jsonStream }) {
|
|
38959
|
+
const args = ["-p", message];
|
|
38960
|
+
if (model) args.push("--model", model);
|
|
38961
|
+
if (resume) args.push("--resume", resume);
|
|
38962
|
+
if (bypassApprovals) args.push("--always-approve");
|
|
38963
|
+
if (jsonStream) args.push("--output-format", "streaming-json");
|
|
38964
|
+
return args;
|
|
38965
|
+
},
|
|
38966
|
+
sessionIdFromStreamEvent(event) {
|
|
38967
|
+
const fromParams = event?.params?.sessionId;
|
|
38968
|
+
if (typeof fromParams === "string" && fromParams) return fromParams;
|
|
38969
|
+
if (typeof event?.sessionId === "string" && event.sessionId) return event.sessionId;
|
|
38970
|
+
return null;
|
|
38971
|
+
},
|
|
38972
|
+
hooksSupportedOnPlatform() {
|
|
38973
|
+
return true;
|
|
38974
|
+
},
|
|
38975
|
+
daemonStreamCapture: false,
|
|
38976
|
+
// Verified live (2026-07-13): grok -p fires NO plugin hooks - not even
|
|
38977
|
+
// SessionStart/Stop - so headless capture is launcher-driven: pin the
|
|
38978
|
+
// session id, then replay ~/.grok/sessions/<cwd>/<sid>/chat_history.jsonl
|
|
38979
|
+
// through the capture runner after the run.
|
|
38980
|
+
headlessCapture: {
|
|
38981
|
+
sessionIdArgs: (sessionId) => ["--session-id", sessionId]
|
|
38982
|
+
},
|
|
38983
|
+
installHint: "curl -fsSL https://grok.x.ai/install.sh | sh (or see docs.x.ai/grok-build)"
|
|
38984
|
+
};
|
|
38985
|
+
|
|
38986
|
+
// src/agents/index.ts
|
|
38987
|
+
var AGENT_ADAPTERS = [
|
|
38988
|
+
claudeCodeAdapter,
|
|
38989
|
+
codexAdapter,
|
|
38990
|
+
grokAdapter
|
|
38991
|
+
];
|
|
38992
|
+
var AGENT_KEYS = AGENT_ADAPTERS.map((a) => a.key);
|
|
38993
|
+
function getAdapter(key) {
|
|
38994
|
+
const found = AGENT_ADAPTERS.find((a) => a.key === key);
|
|
38995
|
+
if (!found) throw new Error(`Unknown agent "${key}". Valid: ${AGENT_KEYS.join(", ")}`);
|
|
38996
|
+
return found;
|
|
38997
|
+
}
|
|
38998
|
+
function getAdapterBySource(source) {
|
|
38999
|
+
const found = AGENT_ADAPTERS.find((a) => a.source === source);
|
|
39000
|
+
if (!found) throw new Error(`No agent adapter for source "${source}"`);
|
|
39001
|
+
return found;
|
|
39002
|
+
}
|
|
39003
|
+
|
|
39004
|
+
// src/prefs.ts
|
|
39005
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync11 } from "fs";
|
|
39006
|
+
import { homedir as homedir11 } from "os";
|
|
39007
|
+
import { join as join16 } from "path";
|
|
39008
|
+
var PREFS_PATH = join16(homedir11(), ".gipity", "prefs.json");
|
|
39009
|
+
function readPrefs() {
|
|
39010
|
+
if (!existsSync13(PREFS_PATH)) return {};
|
|
39011
|
+
try {
|
|
39012
|
+
const parsed = JSON.parse(readFileSync16(PREFS_PATH, "utf-8"));
|
|
39013
|
+
return {
|
|
39014
|
+
lastAgent: typeof parsed.lastAgent === "string" ? parsed.lastAgent : void 0
|
|
39015
|
+
};
|
|
39016
|
+
} catch {
|
|
39017
|
+
return {};
|
|
39018
|
+
}
|
|
39019
|
+
}
|
|
39020
|
+
function writePrefs(update) {
|
|
39021
|
+
try {
|
|
39022
|
+
const current = readPrefs();
|
|
39023
|
+
const next = { ...current, ...update };
|
|
39024
|
+
mkdirSync11(join16(homedir11(), ".gipity"), { recursive: true });
|
|
39025
|
+
writeFileSync11(PREFS_PATH, JSON.stringify(next, null, 2) + "\n");
|
|
39026
|
+
} catch {
|
|
39027
|
+
}
|
|
39028
|
+
}
|
|
39029
|
+
|
|
39030
|
+
// src/commands/build.ts
|
|
38617
39031
|
var __clDir = dirname10(fileURLToPath2(import.meta.url));
|
|
38618
39032
|
function readOwnPkg(fromDir) {
|
|
38619
39033
|
for (let d = fromDir; ; d = dirname10(d)) {
|
|
38620
|
-
const p =
|
|
38621
|
-
if (
|
|
39034
|
+
const p = join17(d, "package.json");
|
|
39035
|
+
if (existsSync14(p)) {
|
|
38622
39036
|
try {
|
|
38623
|
-
const pkg2 = JSON.parse(
|
|
39037
|
+
const pkg2 = JSON.parse(readFileSync17(p, "utf-8"));
|
|
38624
39038
|
if (pkg2.name === "gipity") return pkg2;
|
|
38625
39039
|
} catch {
|
|
38626
39040
|
}
|
|
@@ -38669,12 +39083,12 @@ function localFsFallback(dir) {
|
|
|
38669
39083
|
const topLevelEntries = [];
|
|
38670
39084
|
const walk = (d, depth) => {
|
|
38671
39085
|
try {
|
|
38672
|
-
for (const name of
|
|
39086
|
+
for (const name of readdirSync8(d).sort()) {
|
|
38673
39087
|
if (isSyncIgnored(name)) continue;
|
|
38674
39088
|
let isDir = false;
|
|
38675
39089
|
let size = 0;
|
|
38676
39090
|
try {
|
|
38677
|
-
const st2 = statSync7(
|
|
39091
|
+
const st2 = statSync7(join17(d, name));
|
|
38678
39092
|
isDir = st2.isDirectory();
|
|
38679
39093
|
size = st2.isFile() ? st2.size : 0;
|
|
38680
39094
|
} catch {
|
|
@@ -38683,7 +39097,7 @@ function localFsFallback(dir) {
|
|
|
38683
39097
|
if (isDir) {
|
|
38684
39098
|
folderCount++;
|
|
38685
39099
|
if (depth === 0) topLevelEntries.push(`${name}/`);
|
|
38686
|
-
walk(
|
|
39100
|
+
walk(join17(d, name), depth + 1);
|
|
38687
39101
|
} else {
|
|
38688
39102
|
fileCount++;
|
|
38689
39103
|
totalBytes += size;
|
|
@@ -38716,8 +39130,8 @@ function hasStreamJsonFlag(args) {
|
|
|
38716
39130
|
}
|
|
38717
39131
|
function markFolderTrusted(dir) {
|
|
38718
39132
|
try {
|
|
38719
|
-
const file =
|
|
38720
|
-
const cfg =
|
|
39133
|
+
const file = join17(homedir12(), ".claude.json");
|
|
39134
|
+
const cfg = existsSync14(file) ? JSON.parse(readFileSync17(file, "utf-8")) : {};
|
|
38721
39135
|
if (typeof cfg !== "object" || cfg === null) return;
|
|
38722
39136
|
if (typeof cfg.projects !== "object" || cfg.projects === null) cfg.projects = {};
|
|
38723
39137
|
const entry = typeof cfg.projects[dir] === "object" && cfg.projects[dir] !== null ? cfg.projects[dir] : {};
|
|
@@ -38725,7 +39139,7 @@ function markFolderTrusted(dir) {
|
|
|
38725
39139
|
entry.hasTrustDialogAccepted = true;
|
|
38726
39140
|
cfg.projects[dir] = entry;
|
|
38727
39141
|
const tmp = `${file}.gipity-tmp`;
|
|
38728
|
-
|
|
39142
|
+
writeFileSync12(tmp, JSON.stringify(cfg, null, 2));
|
|
38729
39143
|
renameSync2(tmp, file);
|
|
38730
39144
|
} catch {
|
|
38731
39145
|
}
|
|
@@ -38747,10 +39161,22 @@ function suggestProjectName(existingSlugs) {
|
|
|
38747
39161
|
}
|
|
38748
39162
|
return `project-${Date.now().toString(36).slice(-6)}`;
|
|
38749
39163
|
}
|
|
38750
|
-
|
|
39164
|
+
function createLaunchCommand(name, cfg = {}) {
|
|
39165
|
+
const cmd = new Command(name).description(cfg.presetAgent ? "Set up and run Claude Code (legacy alias of `gipity build`)" : "Start building from anywhere - pick a project, pick your coding agent, go").option("--setup-only", "Do the Gipity setup but skip launching the agent").option("--new-project", "Create a fresh Gipity project instead of using cwd or the picker").option("--name <name>", "Name for --new-project (default: project-NNN)").option("--project <slug>", "Open an existing project by slug or id").option("--here", "Use the current directory instead of ~/GipityProjects/<slug>/").option("--quiet", "Suppress the agent's live progress output (headless --new-project/--project runs)").option("--model <model>", "Model for the session, passed straight to the agent (e.g. opus) - omit it and the agent uses its own default").option("--bypass-approvals", "Skip the agent's interactive tool approvals (headless runs; the relay daemon sets this)").allowUnknownOption(true).allowExcessArguments(true);
|
|
39166
|
+
if (!cfg.presetAgent) {
|
|
39167
|
+
cmd.option("--agent <agent>", `Which coding agent to launch: ${AGENT_KEYS.join(", ")} (default: your last-used)`);
|
|
39168
|
+
}
|
|
39169
|
+
cmd.action(async (opts) => {
|
|
39170
|
+
await runLaunch(name, cfg, opts);
|
|
39171
|
+
});
|
|
39172
|
+
return cmd;
|
|
39173
|
+
}
|
|
39174
|
+
var buildCommand = createLaunchCommand("build");
|
|
39175
|
+
var claudeCommand = createLaunchCommand("claude", { presetAgent: "claude" });
|
|
39176
|
+
async function runLaunch(cmdName, cmdCfg, opts) {
|
|
38751
39177
|
try {
|
|
38752
39178
|
const runStart = Date.now();
|
|
38753
|
-
const rawArgs = process.argv.slice(process.argv.indexOf(
|
|
39179
|
+
const rawArgs = process.argv.slice(process.argv.indexOf(cmdName) + 1);
|
|
38754
39180
|
if (rawArgs[0] === "install") {
|
|
38755
39181
|
const r = ensureClaudeInstalled({ force: rawArgs.includes("--force") });
|
|
38756
39182
|
if (r.alreadyPresent) console.log(` ${success("Claude Code already installed.")}`);
|
|
@@ -38791,7 +39217,7 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
38791
39217
|
process.exit(1);
|
|
38792
39218
|
}
|
|
38793
39219
|
if (nonInteractive && !getConfig() && !opts.newProject && !opts.project) {
|
|
38794
|
-
console.error(` ${error("No Gipity project in cwd. Run `gipity
|
|
39220
|
+
console.error(` ${error("No Gipity project in cwd. Run `gipity build` (interactive) first, or pass --new-project / --project <slug>.")}`);
|
|
38795
39221
|
process.exit(1);
|
|
38796
39222
|
}
|
|
38797
39223
|
if (!nonInteractive) {
|
|
@@ -38830,7 +39256,7 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
38830
39256
|
let existing = getConfig();
|
|
38831
39257
|
let forceAdoptCwd = false;
|
|
38832
39258
|
if (existing && !opts.newProject && !opts.project && !nonInteractive) {
|
|
38833
|
-
const cwdHasConfig =
|
|
39259
|
+
const cwdHasConfig = existsSync14(resolve14(process.cwd(), ".gipity.json"));
|
|
38834
39260
|
if (!cwdHasConfig && isLikelyEmpty(process.cwd())) {
|
|
38835
39261
|
const ancestorRoot = dirname10(getConfigPath());
|
|
38836
39262
|
console.log(` ${bold("You are inside an existing Gipity project.")}
|
|
@@ -38879,8 +39305,8 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
38879
39305
|
}
|
|
38880
39306
|
project = found;
|
|
38881
39307
|
}
|
|
38882
|
-
const projectDir2 = opts.here ? process.cwd() :
|
|
38883
|
-
|
|
39308
|
+
const projectDir2 = opts.here ? process.cwd() : join17(getProjectsRoot(), project.slug);
|
|
39309
|
+
mkdirSync12(projectDir2, { recursive: true });
|
|
38884
39310
|
process.chdir(projectDir2);
|
|
38885
39311
|
clearConfigCache();
|
|
38886
39312
|
let agentGuid = "";
|
|
@@ -38907,10 +39333,7 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
38907
39333
|
} catch (err) {
|
|
38908
39334
|
console.log(` ${warning("Could not sync files (will retry on next prompt):")} ${err.message}`);
|
|
38909
39335
|
}
|
|
38910
|
-
|
|
38911
|
-
setupClaudeMd();
|
|
38912
|
-
setupAgentsMd();
|
|
38913
|
-
setupGitignore();
|
|
39336
|
+
setupProjectTools();
|
|
38914
39337
|
if (nonInteractive) {
|
|
38915
39338
|
headlessNewProject = isNewProject;
|
|
38916
39339
|
} else {
|
|
@@ -38922,10 +39345,7 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
38922
39345
|
console.log(` Project: ${brand(existing.projectSlug)} ${muted(`(${existing.projectGuid})`)}`);
|
|
38923
39346
|
console.log(` ${success("Already set up.")}
|
|
38924
39347
|
`);
|
|
38925
|
-
|
|
38926
|
-
setupClaudeMd();
|
|
38927
|
-
setupAgentsMd();
|
|
38928
|
-
setupGitignore();
|
|
39348
|
+
setupProjectTools();
|
|
38929
39349
|
let doSync = true;
|
|
38930
39350
|
const projectRoot = dirname10(getConfigPath());
|
|
38931
39351
|
const scan = scanForAdoption(projectRoot);
|
|
@@ -39008,8 +39428,8 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
39008
39428
|
} else {
|
|
39009
39429
|
project = result.project;
|
|
39010
39430
|
isNewProject = result.kind === "create-new";
|
|
39011
|
-
const projectDir2 =
|
|
39012
|
-
|
|
39431
|
+
const projectDir2 = join17(getProjectsRoot(), project.slug);
|
|
39432
|
+
mkdirSync12(projectDir2, { recursive: true });
|
|
39013
39433
|
process.chdir(projectDir2);
|
|
39014
39434
|
clearConfigCache();
|
|
39015
39435
|
try {
|
|
@@ -39037,15 +39457,40 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
39037
39457
|
}
|
|
39038
39458
|
}
|
|
39039
39459
|
initialPrompt = "";
|
|
39040
|
-
|
|
39041
|
-
setupClaudeMd();
|
|
39042
|
-
setupAgentsMd();
|
|
39043
|
-
setupGitignore();
|
|
39460
|
+
setupProjectTools();
|
|
39044
39461
|
console.log(` ${success(`Project "${project.name}" ready.`)}
|
|
39045
39462
|
`);
|
|
39046
39463
|
}
|
|
39047
39464
|
if (opts.setupOnly) {
|
|
39048
|
-
console.log(` Done. cd ${process.cwd()} && gipity
|
|
39465
|
+
console.log(` Done. cd ${process.cwd()} && gipity ${cmdName}`);
|
|
39466
|
+
return;
|
|
39467
|
+
}
|
|
39468
|
+
const prefs = readPrefs();
|
|
39469
|
+
let agentKey = cmdCfg.presetAgent ?? (typeof opts.agent === "string" ? opts.agent.toLowerCase() : void 0);
|
|
39470
|
+
if (agentKey && !AGENT_KEYS.includes(agentKey)) {
|
|
39471
|
+
console.error(` ${error(`Unknown agent "${agentKey}".`)} ${muted(`Valid: ${AGENT_KEYS.join(", ")}`)}`);
|
|
39472
|
+
process.exit(1);
|
|
39473
|
+
}
|
|
39474
|
+
if (!agentKey) {
|
|
39475
|
+
if (nonInteractive) {
|
|
39476
|
+
agentKey = prefs.lastAgent && AGENT_KEYS.includes(prefs.lastAgent) ? prefs.lastAgent : "claude";
|
|
39477
|
+
} else {
|
|
39478
|
+
agentKey = await pickAgent(prefs.lastAgent);
|
|
39479
|
+
}
|
|
39480
|
+
}
|
|
39481
|
+
const adapter = getAdapter(agentKey);
|
|
39482
|
+
if (!nonInteractive && !cmdCfg.presetAgent) {
|
|
39483
|
+
writePrefs({ lastAgent: adapter.key });
|
|
39484
|
+
}
|
|
39485
|
+
if (adapter.key !== "claude") {
|
|
39486
|
+
await launchNonClaudeAgent(adapter, {
|
|
39487
|
+
cmdName,
|
|
39488
|
+
opts,
|
|
39489
|
+
rawArgs,
|
|
39490
|
+
nonInteractive,
|
|
39491
|
+
headlessOut,
|
|
39492
|
+
runStart
|
|
39493
|
+
});
|
|
39049
39494
|
return;
|
|
39050
39495
|
}
|
|
39051
39496
|
if (!isClaudeInstalled()) {
|
|
@@ -39071,7 +39516,7 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
39071
39516
|
if (resumeSid) {
|
|
39072
39517
|
try {
|
|
39073
39518
|
const found = await get(
|
|
39074
|
-
`/conversations/
|
|
39519
|
+
`/conversations/remote/by-session/${encodeURIComponent(resumeSid)}?source=claude_code`
|
|
39075
39520
|
);
|
|
39076
39521
|
convGuidForHooks = found.data.conversation_guid;
|
|
39077
39522
|
} catch {
|
|
@@ -39079,8 +39524,8 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
39079
39524
|
}
|
|
39080
39525
|
if (!convGuidForHooks && cfg?.projectGuid) {
|
|
39081
39526
|
const created = await post(
|
|
39082
|
-
"/conversations/
|
|
39083
|
-
{ project_guid: cfg.projectGuid, device_guid: device.guid, origin: "local" }
|
|
39527
|
+
"/conversations/remote",
|
|
39528
|
+
{ project_guid: cfg.projectGuid, device_guid: device.guid, source: "claude_code", origin: "local" }
|
|
39084
39529
|
);
|
|
39085
39530
|
convGuidForHooks = created.data.conversation_guid;
|
|
39086
39531
|
}
|
|
@@ -39091,10 +39536,10 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
39091
39536
|
}
|
|
39092
39537
|
}
|
|
39093
39538
|
}
|
|
39094
|
-
const
|
|
39095
|
-
const rawClaudeArgs = process.argv.slice(
|
|
39096
|
-
const gipityBooleanFlags = /* @__PURE__ */ new Set(["--setup-only", "--new-project", "--here", "--quiet"]);
|
|
39097
|
-
const gipityValueFlags = ["--api-base", "--name", "--project"];
|
|
39539
|
+
const cmdIdx = process.argv.indexOf(cmdName);
|
|
39540
|
+
const rawClaudeArgs = process.argv.slice(cmdIdx + 1);
|
|
39541
|
+
const gipityBooleanFlags = /* @__PURE__ */ new Set(["--setup-only", "--new-project", "--here", "--quiet", "--bypass-approvals"]);
|
|
39542
|
+
const gipityValueFlags = ["--api-base", "--name", "--project", "--agent"];
|
|
39098
39543
|
const claudeArgs = [];
|
|
39099
39544
|
for (let i = 0; i < rawClaudeArgs.length; i++) {
|
|
39100
39545
|
const arg = rawClaudeArgs[i];
|
|
@@ -39110,6 +39555,9 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
39110
39555
|
console.log(` ${bold("Launching Claude Code, powered by Gipity.")}`);
|
|
39111
39556
|
console.log(` ${muted("Just tell Claude what you'd like to build or do - everything Claude can do,")}`);
|
|
39112
39557
|
console.log(` ${muted("plus hosting, databases, and live deploys on Gipity.")}`);
|
|
39558
|
+
if (convGuidForHooks) {
|
|
39559
|
+
console.log(` ${muted(`This session is saved to Gipity - watch it live at ${brand("prompt.gipity.ai")}. (--no-capture on init to disable.)`)}`);
|
|
39560
|
+
}
|
|
39113
39561
|
console.log("");
|
|
39114
39562
|
}
|
|
39115
39563
|
let allArgs;
|
|
@@ -39186,8 +39634,11 @@ Sending to Claude Code: ${userMsg}
|
|
|
39186
39634
|
${doneLine}
|
|
39187
39635
|
|
|
39188
39636
|
`);
|
|
39189
|
-
else
|
|
39637
|
+
else {
|
|
39638
|
+
console.log(`
|
|
39190
39639
|
${doneLine}`);
|
|
39640
|
+
console.log(` ${muted(`Next time: gipity build from anywhere, or cd ${process.cwd()} && claude.`)}`);
|
|
39641
|
+
}
|
|
39191
39642
|
process.exit(code ?? 0);
|
|
39192
39643
|
});
|
|
39193
39644
|
} catch (err) {
|
|
@@ -39195,7 +39646,186 @@ ${doneLine}
|
|
|
39195
39646
|
${error(`Error: ${err.message}`)}`);
|
|
39196
39647
|
process.exit(1);
|
|
39197
39648
|
}
|
|
39198
|
-
}
|
|
39649
|
+
}
|
|
39650
|
+
async function pickAgent(lastUsed) {
|
|
39651
|
+
const defaultKey = lastUsed && AGENT_KEYS.includes(lastUsed) ? lastUsed : "claude";
|
|
39652
|
+
const defaultIdx = AGENT_ADAPTERS.findIndex((a) => a.key === defaultKey) + 1;
|
|
39653
|
+
console.log(` ${bold("Which coding agent?")}
|
|
39654
|
+
`);
|
|
39655
|
+
AGENT_ADAPTERS.forEach((a, i) => {
|
|
39656
|
+
const installed = binaryOnPath(a.binary) || a.key === "claude" && isClaudeInstalled();
|
|
39657
|
+
const notes = [];
|
|
39658
|
+
if (a.key === lastUsed) notes.push("last used");
|
|
39659
|
+
if (!installed) notes.push(a.ensureInstalled ? "not installed - we'll install it" : "not installed");
|
|
39660
|
+
if (a.key === "codex" && !a.hooksSupportedOnPlatform(process.platform)) {
|
|
39661
|
+
notes.push("session recording unavailable on Windows");
|
|
39662
|
+
}
|
|
39663
|
+
const note = notes.length ? ` ${muted(`(${notes.join(", ")})`)}` : "";
|
|
39664
|
+
console.log(` ${bold(`${i + 1}.`)} ${a.displayName} ${muted(`(${a.providerName})`)}${note}`);
|
|
39665
|
+
});
|
|
39666
|
+
console.log("");
|
|
39667
|
+
const choice = await pickOne("Choose", AGENT_ADAPTERS.length, defaultIdx);
|
|
39668
|
+
console.log("");
|
|
39669
|
+
return AGENT_ADAPTERS[choice - 1].key;
|
|
39670
|
+
}
|
|
39671
|
+
async function launchNonClaudeAgent(adapter, ctx) {
|
|
39672
|
+
const { opts, rawArgs, nonInteractive, headlessOut, runStart } = ctx;
|
|
39673
|
+
if (!binaryOnPath(adapter.binary)) {
|
|
39674
|
+
let installed = false;
|
|
39675
|
+
if (adapter.ensureInstalled) {
|
|
39676
|
+
console.log(` ${adapter.displayName} not found - installing it now...`);
|
|
39677
|
+
installed = adapter.ensureInstalled();
|
|
39678
|
+
}
|
|
39679
|
+
if (!installed && !binaryOnPath(adapter.binary)) {
|
|
39680
|
+
console.error(` ${error(`${adapter.displayName} is not installed.`)} Install it with: ${adapter.installHint}`);
|
|
39681
|
+
console.error(` ${muted(`Then run: gipity ${ctx.cmdName} --agent ${adapter.key}`)}`);
|
|
39682
|
+
process.exit(1);
|
|
39683
|
+
}
|
|
39684
|
+
}
|
|
39685
|
+
const booleanFlags = /* @__PURE__ */ new Set(["--setup-only", "--new-project", "--here", "--quiet", "--bypass-approvals"]);
|
|
39686
|
+
const valueFlags = ["--api-base", "--name", "--project", "--agent"];
|
|
39687
|
+
let message = null;
|
|
39688
|
+
let resume;
|
|
39689
|
+
let model;
|
|
39690
|
+
const extras = [];
|
|
39691
|
+
for (let i = 0; i < rawArgs.length; i++) {
|
|
39692
|
+
const arg = rawArgs[i];
|
|
39693
|
+
if (booleanFlags.has(arg)) continue;
|
|
39694
|
+
if (valueFlags.includes(arg)) {
|
|
39695
|
+
i++;
|
|
39696
|
+
continue;
|
|
39697
|
+
}
|
|
39698
|
+
if (valueFlags.some((f) => arg.startsWith(`${f}=`))) continue;
|
|
39699
|
+
if (arg === "-p" || arg === "--print") {
|
|
39700
|
+
message = rawArgs[++i] ?? "";
|
|
39701
|
+
continue;
|
|
39702
|
+
}
|
|
39703
|
+
if (arg.startsWith("-p=") || arg.startsWith("--print=")) {
|
|
39704
|
+
message = arg.slice(arg.indexOf("=") + 1);
|
|
39705
|
+
continue;
|
|
39706
|
+
}
|
|
39707
|
+
if (arg === "--model") {
|
|
39708
|
+
model = rawArgs[++i];
|
|
39709
|
+
continue;
|
|
39710
|
+
}
|
|
39711
|
+
if (arg.startsWith("--model=")) {
|
|
39712
|
+
model = arg.slice("--model=".length);
|
|
39713
|
+
continue;
|
|
39714
|
+
}
|
|
39715
|
+
if (arg === "--resume") {
|
|
39716
|
+
resume = rawArgs[++i];
|
|
39717
|
+
continue;
|
|
39718
|
+
}
|
|
39719
|
+
if (arg.startsWith("--resume=")) {
|
|
39720
|
+
resume = arg.slice("--resume=".length);
|
|
39721
|
+
continue;
|
|
39722
|
+
}
|
|
39723
|
+
extras.push(arg);
|
|
39724
|
+
}
|
|
39725
|
+
let convGuid = process.env.GIPITY_CONVERSATION_GUID ?? null;
|
|
39726
|
+
if (!convGuid) {
|
|
39727
|
+
const device = getDevice();
|
|
39728
|
+
const config = getConfig();
|
|
39729
|
+
if (device && config?.projectGuid) {
|
|
39730
|
+
try {
|
|
39731
|
+
if (resume) {
|
|
39732
|
+
try {
|
|
39733
|
+
const found = await get(
|
|
39734
|
+
`/conversations/remote/by-session/${encodeURIComponent(resume)}?source=${encodeURIComponent(adapter.source)}`
|
|
39735
|
+
);
|
|
39736
|
+
convGuid = found.data.conversation_guid;
|
|
39737
|
+
} catch {
|
|
39738
|
+
}
|
|
39739
|
+
}
|
|
39740
|
+
if (!convGuid) {
|
|
39741
|
+
const created = await post(
|
|
39742
|
+
"/conversations/remote",
|
|
39743
|
+
{ project_guid: config.projectGuid, device_guid: device.guid, source: adapter.source, origin: "local" }
|
|
39744
|
+
);
|
|
39745
|
+
convGuid = created.data.conversation_guid;
|
|
39746
|
+
}
|
|
39747
|
+
} catch (err) {
|
|
39748
|
+
if (!nonInteractive) {
|
|
39749
|
+
console.error(` ${error(`Could not create Gipity conversation: ${err?.message || err}`)}`);
|
|
39750
|
+
}
|
|
39751
|
+
}
|
|
39752
|
+
}
|
|
39753
|
+
}
|
|
39754
|
+
let agentArgs;
|
|
39755
|
+
let syntheticSid = null;
|
|
39756
|
+
if (nonInteractive) {
|
|
39757
|
+
const bypass = Boolean(opts.bypassApprovals || opts.newProject || opts.project);
|
|
39758
|
+
agentArgs = [
|
|
39759
|
+
...adapter.buildHeadlessArgs({ message: message ?? "", resume, model, bypassApprovals: bypass, jsonStream: false }),
|
|
39760
|
+
...extras
|
|
39761
|
+
];
|
|
39762
|
+
if (adapter.headlessCapture) {
|
|
39763
|
+
syntheticSid = resume ?? randomUUID();
|
|
39764
|
+
if (!resume) agentArgs.push(...adapter.headlessCapture.sessionIdArgs(syntheticSid));
|
|
39765
|
+
}
|
|
39766
|
+
if (message) headlessOut(`
|
|
39767
|
+
Sending to ${adapter.displayName}: ${message}
|
|
39768
|
+
`);
|
|
39769
|
+
} else {
|
|
39770
|
+
agentArgs = [...adapter.buildInteractiveArgs({ resume, model }), ...extras];
|
|
39771
|
+
console.log(` ${bold(`Launching ${adapter.displayName}, powered by Gipity.`)}`);
|
|
39772
|
+
console.log(` ${muted(`Just tell ${adapter.displayName} what you'd like to build or do - plus hosting,`)}`);
|
|
39773
|
+
console.log(` ${muted("databases, and live deploys on Gipity.")}`);
|
|
39774
|
+
if (convGuid) {
|
|
39775
|
+
console.log(` ${muted(`This session is saved to Gipity - watch it live at ${brand("prompt.gipity.ai")}. (--no-capture on init to disable.)`)}`);
|
|
39776
|
+
}
|
|
39777
|
+
if (adapter.oneTimeSetupNote) {
|
|
39778
|
+
console.log(` ${warning(adapter.oneTimeSetupNote)}`);
|
|
39779
|
+
}
|
|
39780
|
+
console.log("");
|
|
39781
|
+
}
|
|
39782
|
+
const childEnv2 = { ...process.env };
|
|
39783
|
+
if (convGuid) childEnv2.GIPITY_CONVERSATION_GUID = convGuid;
|
|
39784
|
+
const agentCmd = resolveCommand(adapter.binary);
|
|
39785
|
+
const child = spawnCommand(agentCmd, agentArgs, {
|
|
39786
|
+
stdio: "inherit",
|
|
39787
|
+
cwd: process.cwd(),
|
|
39788
|
+
env: childEnv2
|
|
39789
|
+
});
|
|
39790
|
+
child.on("error", (err) => {
|
|
39791
|
+
console.error(`
|
|
39792
|
+
${error(err.code === "ENOENT" ? `${adapter.displayName} not found. Install it: ${adapter.installHint}` : `Failed to launch ${adapter.displayName}: ${err.message}`)}`);
|
|
39793
|
+
process.exit(1);
|
|
39794
|
+
});
|
|
39795
|
+
child.on("exit", (code) => {
|
|
39796
|
+
if (syntheticSid && convGuid) {
|
|
39797
|
+
flushHeadlessCapture(adapter, syntheticSid, convGuid);
|
|
39798
|
+
}
|
|
39799
|
+
const doneLine = `Done (${formatElapsed2(Date.now() - runStart)})`;
|
|
39800
|
+
if (nonInteractive) process.stderr.write(`
|
|
39801
|
+
${doneLine}
|
|
39802
|
+
|
|
39803
|
+
`);
|
|
39804
|
+
else {
|
|
39805
|
+
console.log(`
|
|
39806
|
+
${doneLine}`);
|
|
39807
|
+
console.log(` ${muted(`Next time: gipity build from anywhere, or cd ${process.cwd()} && ${adapter.binary}.`)}`);
|
|
39808
|
+
}
|
|
39809
|
+
process.exit(code ?? 0);
|
|
39810
|
+
});
|
|
39811
|
+
}
|
|
39812
|
+
function flushHeadlessCapture(adapter, sessionId, convGuid) {
|
|
39813
|
+
const runner = join17(__clDir, "..", "hooks", "capture-runner.js");
|
|
39814
|
+
if (!existsSync14(runner)) return;
|
|
39815
|
+
const payload = JSON.stringify({ session_id: sessionId, cwd: process.cwd() });
|
|
39816
|
+
const env = { ...process.env, GIPITY_CONVERSATION_GUID: convGuid };
|
|
39817
|
+
for (const event of ["session-start", "stop"]) {
|
|
39818
|
+
try {
|
|
39819
|
+
spawnSyncCommand(process.execPath, [runner, adapter.key, event], {
|
|
39820
|
+
input: payload,
|
|
39821
|
+
env,
|
|
39822
|
+
timeout: 6e4,
|
|
39823
|
+
stdio: ["pipe", "ignore", "ignore"]
|
|
39824
|
+
});
|
|
39825
|
+
} catch {
|
|
39826
|
+
}
|
|
39827
|
+
}
|
|
39828
|
+
}
|
|
39199
39829
|
async function pickOrCreateProject(projects, existingSlugs) {
|
|
39200
39830
|
const filtered = projects.filter((p) => !p.is_default);
|
|
39201
39831
|
const cwd = process.cwd();
|
|
@@ -39256,9 +39886,9 @@ async function pickFromAll(filtered) {
|
|
|
39256
39886
|
function formatNewProjectLabel(existingSlugs) {
|
|
39257
39887
|
const slug = suggestProjectName(existingSlugs);
|
|
39258
39888
|
const root = getProjectsRoot();
|
|
39259
|
-
const home =
|
|
39260
|
-
const display2 = root === home || root.startsWith(home +
|
|
39261
|
-
return `${display2}${
|
|
39889
|
+
const home = homedir12();
|
|
39890
|
+
const display2 = root === home || root.startsWith(home + sep5) ? "~" + root.slice(home.length) : root;
|
|
39891
|
+
return `${display2}${sep5}${slug}`;
|
|
39262
39892
|
}
|
|
39263
39893
|
async function confirmAdoptCwd(cwd) {
|
|
39264
39894
|
process.stdout.write(` ${muted("Scanning directory...")} `);
|
|
@@ -39335,18 +39965,23 @@ import os from "os";
|
|
|
39335
39965
|
init_api();
|
|
39336
39966
|
init_config();
|
|
39337
39967
|
init_colors();
|
|
39968
|
+
|
|
39969
|
+
// src/catalog.ts
|
|
39338
39970
|
var STARTERS = [
|
|
39339
39971
|
{ key: "web-vision-cam", hint: "fullscreen camera app with on-device vision (MediaPipe)" },
|
|
39340
39972
|
{ key: "object-spotter", hint: "camera app that boxes, labels, and counts objects (YOLOX on-device)" },
|
|
39341
39973
|
{ key: "2d-game", hint: "2D games with Phaser 3 - platformer, arcade, puzzle" },
|
|
39342
39974
|
{ key: "3d-world", hint: "playable 3D multiplayer rocket-launcher demo" },
|
|
39343
|
-
{ key: "karaoke-captions", hint: "audio + lyrics -> word-synced karaoke captions (GPU job)" }
|
|
39975
|
+
{ key: "karaoke-captions", hint: "audio + lyrics -> word-synced karaoke captions (GPU job)" },
|
|
39976
|
+
{ key: "outreach-agent", hint: "AI-run outreach funnel - import contacts, draft + auto-send staged emails" },
|
|
39977
|
+
{ key: "paid-app", hint: "storefront that charges real money - Stripe checkout, members area, billing" },
|
|
39978
|
+
{ key: "notify-demo", hint: "web-push demo - enable notifications, send a real ping" }
|
|
39344
39979
|
];
|
|
39345
39980
|
var BLANK = [
|
|
39346
39981
|
{ key: "web-simple", hint: "static frontend-only site - pages, dashboards, simple games" },
|
|
39347
39982
|
{ key: "web-fullstack", hint: "backend API + database wiring - frontend, functions, migrations; deploys green" },
|
|
39348
|
-
{ key: "
|
|
39349
|
-
{ key: "
|
|
39983
|
+
{ key: "3d-engine", hint: "3D multiplayer wiring - Three.js + Rapier + Gipity Realtime" },
|
|
39984
|
+
{ key: "api", hint: "pure API backend, no frontend - one example function + test" }
|
|
39350
39985
|
];
|
|
39351
39986
|
var KITS = [
|
|
39352
39987
|
{ key: "realtime", hint: "multiplayer / presence / shared state" },
|
|
@@ -39354,8 +39989,16 @@ var KITS = [
|
|
|
39354
39989
|
{ key: "web-vision-detect", hint: "browser object detection - YOLOX, WebGPU/WASM, custom models" },
|
|
39355
39990
|
{ key: "chatbot", hint: "drop-in chatbot - persona, guardrails, streaming responses" },
|
|
39356
39991
|
{ key: "audio-align", hint: "audio + lyrics -> word-level timing JSON (GPU job)" },
|
|
39357
|
-
{ key: "i18n", hint: "multi-language web apps - language picker, RTL, translations" }
|
|
39992
|
+
{ key: "i18n", hint: "multi-language web apps - language picker, RTL, translations" },
|
|
39993
|
+
{ key: "records", hint: "registry-driven data plane - generic CRUD, validation, search, audit spine" },
|
|
39994
|
+
{ key: "views", hint: "registry-driven UI over records - table, forms, kanban" },
|
|
39995
|
+
{ key: "agent-api", hint: "named API keys for agent/script writes through the records kit" },
|
|
39996
|
+
{ key: "contacts", hint: "multi-source contact layer - LinkedIn/Gmail import, dedupe with provenance" },
|
|
39997
|
+
{ key: "stripe", hint: "charge your users - Stripe checkout, subscriptions, brokered webhooks" },
|
|
39998
|
+
{ key: "notify", hint: "web push notifications - platform-owned keys, works on iOS home screen" }
|
|
39358
39999
|
];
|
|
40000
|
+
|
|
40001
|
+
// src/commands/add.ts
|
|
39359
40002
|
function catalogText() {
|
|
39360
40003
|
const width = Math.max(...[...STARTERS, ...BLANK, ...KITS].map((e) => e.key.length));
|
|
39361
40004
|
const row2 = (e) => ` ${e.key.padEnd(width)} ${muted(e.hint)}`;
|
|
@@ -39809,7 +40452,19 @@ init_api();
|
|
|
39809
40452
|
init_config();
|
|
39810
40453
|
init_colors();
|
|
39811
40454
|
var logsCommand = new Command("logs").description("View logs");
|
|
39812
|
-
logsCommand.command("fn <name>").description("Show function logs").option("--limit <n>", "Max entries", "20").option("--json", "Output as JSON
|
|
40455
|
+
logsCommand.command("fn <name>").description("Show function logs").option("--limit <n>", "Max entries", "20").option("--json", "Output as JSON (top-level ARRAY of invocations, newest first)").addHelpText("after", `
|
|
40456
|
+
Each line: time, status (ok / error / limit_exceeded), duration, trigger, and
|
|
40457
|
+
svc:N when the invocation made N platform service calls (notify, email, LLM,
|
|
40458
|
+
image, ...). Captured console output is indented beneath its invocation.
|
|
40459
|
+
|
|
40460
|
+
--json emits a top-level ARRAY (not an object) of invocation rows:
|
|
40461
|
+
[{ id, status, duration_ms, trigger_type, error_message,
|
|
40462
|
+
limits_consumed: { max_queries, max_service_calls, ... }, logs: [...], created_at }]
|
|
40463
|
+
|
|
40464
|
+
To confirm an individual service call succeeded (and see provider, latency,
|
|
40465
|
+
and its own error message), use the per-call log instead:
|
|
40466
|
+
$ gipity logs app --type services
|
|
40467
|
+
`).action((name, opts) => run("Logs", async () => {
|
|
39813
40468
|
const config = requireConfig();
|
|
39814
40469
|
const limit = parseInt(opts.limit, 10) || 20;
|
|
39815
40470
|
const res = await get(
|
|
@@ -39830,8 +40485,10 @@ logsCommand.command("fn <name>").description("Show function logs").option("--lim
|
|
|
39830
40485
|
const statusColor = log3.status === "ok" ? success : log3.status === "error" ? error : warning;
|
|
39831
40486
|
const status = statusColor(log3.status.padEnd(8));
|
|
39832
40487
|
const trigger = muted(log3.trigger_type.padEnd(8));
|
|
40488
|
+
const svcCalls = log3.limits_consumed?.max_service_calls;
|
|
40489
|
+
const svc = svcCalls ? ` ${muted(`svc:${svcCalls}`)}` : "";
|
|
39833
40490
|
const err = log3.error_message ? ` ${error(`"${log3.error_message}"`)}` : "";
|
|
39834
|
-
console.log(`${muted(time)} ${status} ${dur} ${trigger}${err}`);
|
|
40491
|
+
console.log(`${muted(time)} ${status} ${dur} ${trigger}${svc}${err}`);
|
|
39835
40492
|
for (const line of log3.logs ?? []) {
|
|
39836
40493
|
const lvlColor = line.level === "error" ? error : line.level === "warn" ? warning : muted;
|
|
39837
40494
|
const tag = line.level === "log" ? "" : `${line.level}: `;
|
|
@@ -40384,6 +41041,7 @@ recordsCommand.command("delete <table> <id>").description("Delete a record").act
|
|
|
40384
41041
|
|
|
40385
41042
|
// src/commands/fn.ts
|
|
40386
41043
|
init_api();
|
|
41044
|
+
init_auth();
|
|
40387
41045
|
init_config();
|
|
40388
41046
|
init_colors();
|
|
40389
41047
|
init_utils();
|
|
@@ -40396,6 +41054,9 @@ fnCommand.command("list").description("List functions").option("--json", "Output
|
|
|
40396
41054
|
return f.description ? `${line}
|
|
40397
41055
|
${muted(f.description)}` : line;
|
|
40398
41056
|
});
|
|
41057
|
+
if (!opts.json && res.data.length > 0) {
|
|
41058
|
+
console.log(muted(`Endpoint: POST ${config.apiBase}/api/${config.projectGuid}/fn/<name> (same on dev and prod; public path: gipity fn call <name> --anon)`));
|
|
41059
|
+
}
|
|
40399
41060
|
}));
|
|
40400
41061
|
fnCommand.command("logs <name>").description("Show recent logs").option("--limit <n>", "Max entries", "20").option("--json", "Output as JSON").action((name, opts) => run("Logs", async () => {
|
|
40401
41062
|
const config = requireConfig();
|
|
@@ -40405,8 +41066,10 @@ fnCommand.command("logs <name>").description("Show recent logs").option("--limit
|
|
|
40405
41066
|
printList(res.data, opts, "No execution logs.", (log3) => {
|
|
40406
41067
|
const dur = log3.duration_ms != null ? `${log3.duration_ms}ms` : "?";
|
|
40407
41068
|
const ts2 = new Date(log3.created_at).toLocaleString();
|
|
40408
|
-
const statusColor = log3.status === "
|
|
41069
|
+
const statusColor = log3.status === "ok" ? success : log3.status === "error" ? error : muted;
|
|
40409
41070
|
let line = `${statusColor(log3.status)} ${dur} ${muted(log3.trigger_type || "http")} ${muted(ts2)}`;
|
|
41071
|
+
const svcCalls = log3.limits_consumed?.max_service_calls;
|
|
41072
|
+
if (svcCalls) line += ` ${muted(`svc:${svcCalls}`)}`;
|
|
40410
41073
|
if (log3.error_message) line += `
|
|
40411
41074
|
${error(`error: ${log3.error_message}`)}`;
|
|
40412
41075
|
for (const entry of log3.logs ?? []) {
|
|
@@ -40436,6 +41099,12 @@ fnCommand.command("call <name> [body]").description("Call a function").option("-
|
|
|
40436
41099
|
const raw = bodyArg || opts.data || "{}";
|
|
40437
41100
|
const body = JSON.parse(raw);
|
|
40438
41101
|
const path5 = `/api/${config.projectGuid}/fn/${encodeURIComponent(name)}`;
|
|
41102
|
+
if (opts.anon) {
|
|
41103
|
+
console.error(muted("Auth: anonymous visitor (the public path a signed-out user hits)"));
|
|
41104
|
+
} else {
|
|
41105
|
+
const who = getAuth()?.email;
|
|
41106
|
+
console.error(muted(`Auth: calling as ${who ?? "your signed-in account"} (the owner persona; use --anon for the public visitor path)`));
|
|
41107
|
+
}
|
|
40439
41108
|
const res = opts.anon ? await callAnon(config.projectGuid, name, body) : await post(path5, body);
|
|
40440
41109
|
if (opts.field) {
|
|
40441
41110
|
emitField(res.data, opts.field);
|
|
@@ -40919,11 +41588,11 @@ jobCommand.command("cancel <runGuid>").description("Cancel a queued or running j
|
|
|
40919
41588
|
}
|
|
40920
41589
|
}));
|
|
40921
41590
|
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) => {
|
|
40922
|
-
const { existsSync:
|
|
40923
|
-
const { resolve:
|
|
41591
|
+
const { existsSync: existsSync24, statSync: statSync10 } = await import("node:fs");
|
|
41592
|
+
const { resolve: resolve20, join: join26 } = await import("node:path");
|
|
40924
41593
|
const { spawnCommand: spawnCommand2, spawnSyncCommand: spawnSyncCommand2 } = await Promise.resolve().then(() => (init_platform(), platform_exports));
|
|
40925
|
-
const jobDir =
|
|
40926
|
-
if (!
|
|
41594
|
+
const jobDir = resolve20(process.cwd(), "jobs", name);
|
|
41595
|
+
if (!existsSync24(jobDir) || !statSync10(jobDir).isDirectory()) {
|
|
40927
41596
|
console.error(error(`jobs/${name}/ not found in current directory`));
|
|
40928
41597
|
process.exit(1);
|
|
40929
41598
|
}
|
|
@@ -40944,7 +41613,7 @@ jobCommand.command("run-local <name>").description("Run the job in a local Docke
|
|
|
40944
41613
|
},
|
|
40945
41614
|
{ file: "main.sh", runtime: "bash", entry: "bash" }
|
|
40946
41615
|
];
|
|
40947
|
-
const picked = variants.find((v7) =>
|
|
41616
|
+
const picked = variants.find((v7) => existsSync24(join26(jobDir, v7.file)));
|
|
40948
41617
|
if (!picked) {
|
|
40949
41618
|
console.error(error(`No main.py / main.js / main.sh found under jobs/${name}/`));
|
|
40950
41619
|
process.exit(1);
|
|
@@ -40969,7 +41638,7 @@ jobCommand.command("run-local <name>").description("Run the job in a local Docke
|
|
|
40969
41638
|
"-v",
|
|
40970
41639
|
`${jobDir}:/work`
|
|
40971
41640
|
];
|
|
40972
|
-
const needsDeps = opts.deps !== false && picked.depsFile && picked.installCmd &&
|
|
41641
|
+
const needsDeps = opts.deps !== false && picked.depsFile && picked.installCmd && existsSync24(join26(jobDir, picked.depsFile));
|
|
40973
41642
|
const handlerCmd = `${picked.entry} /work/${picked.file}`;
|
|
40974
41643
|
let shellCmd;
|
|
40975
41644
|
if (needsDeps && picked.installCmd) {
|
|
@@ -41144,7 +41813,7 @@ emailCommand.command("log").description("Recent email() sends from your app").op
|
|
|
41144
41813
|
init_api();
|
|
41145
41814
|
init_config();
|
|
41146
41815
|
init_colors();
|
|
41147
|
-
import { mkdirSync as
|
|
41816
|
+
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync13 } from "fs";
|
|
41148
41817
|
import { resolve as resolvePath2, dirname as dirname11, relative as relative5, isAbsolute } from "path";
|
|
41149
41818
|
|
|
41150
41819
|
// src/provider-docs.ts
|
|
@@ -41165,7 +41834,7 @@ async function downloadFile(url, filename) {
|
|
|
41165
41834
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
41166
41835
|
const outPath = correctExtension(filename, buffer);
|
|
41167
41836
|
ensureOutputDir(outPath);
|
|
41168
|
-
|
|
41837
|
+
writeFileSync13(outPath, buffer);
|
|
41169
41838
|
const savedPath = resolvePath2(outPath);
|
|
41170
41839
|
await pushGenerated(savedPath);
|
|
41171
41840
|
return savedPath;
|
|
@@ -41201,7 +41870,7 @@ function correctExtension(filename, buf) {
|
|
|
41201
41870
|
function ensureOutputDir(output) {
|
|
41202
41871
|
const dir = dirname11(resolvePath2(output));
|
|
41203
41872
|
try {
|
|
41204
|
-
|
|
41873
|
+
mkdirSync13(dir, { recursive: true });
|
|
41205
41874
|
} catch (err) {
|
|
41206
41875
|
throw new Error(`can't create the output directory ${dir} (${err.code || err.message}). Pick a different -o path.`);
|
|
41207
41876
|
}
|
|
@@ -41434,14 +42103,14 @@ var generateCommand = new Command("generate").description("Generate images, vide
|
|
|
41434
42103
|
init_api();
|
|
41435
42104
|
init_config();
|
|
41436
42105
|
init_colors();
|
|
41437
|
-
import { existsSync as
|
|
41438
|
-
import { join as
|
|
42106
|
+
import { existsSync as existsSync15, readFileSync as readFileSync18 } from "fs";
|
|
42107
|
+
import { join as join18 } from "path";
|
|
41439
42108
|
function readInstalledKitReadme(name) {
|
|
41440
42109
|
const root = getProjectRoot() ?? process.cwd();
|
|
41441
|
-
const kitDir =
|
|
41442
|
-
if (!
|
|
41443
|
-
const readme =
|
|
41444
|
-
return
|
|
42110
|
+
const kitDir = join18(root, "src", "packages", name);
|
|
42111
|
+
if (!existsSync15(join18(kitDir, "package.json"))) return null;
|
|
42112
|
+
const readme = join18(kitDir, "README.md");
|
|
42113
|
+
return existsSync15(readme) ? readFileSync18(readme, "utf-8") : null;
|
|
41445
42114
|
}
|
|
41446
42115
|
var skillCommand = new Command("skill").description("Task docs - read the matching skill before building");
|
|
41447
42116
|
skillCommand.command("list").description("List skills").option("--json", "Output as JSON").action((opts) => run("List", async () => {
|
|
@@ -41614,7 +42283,7 @@ var domainCommand = new Command("domain").description("Manage custom domains").a
|
|
|
41614
42283
|
}));
|
|
41615
42284
|
|
|
41616
42285
|
// src/commands/realtime.ts
|
|
41617
|
-
import { existsSync as
|
|
42286
|
+
import { existsSync as existsSync16 } from "fs";
|
|
41618
42287
|
import { dirname as dirname12, resolve as resolve15 } from "path";
|
|
41619
42288
|
init_api();
|
|
41620
42289
|
init_config();
|
|
@@ -41622,7 +42291,7 @@ init_colors();
|
|
|
41622
42291
|
function hasDeployManifest() {
|
|
41623
42292
|
const cfgPath = getConfigPath();
|
|
41624
42293
|
if (!cfgPath) return false;
|
|
41625
|
-
return
|
|
42294
|
+
return existsSync16(resolve15(dirname12(cfgPath), "gipity.yaml"));
|
|
41626
42295
|
}
|
|
41627
42296
|
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 () => {
|
|
41628
42297
|
const config = requireConfig();
|
|
@@ -41798,7 +42467,7 @@ async function pollTestStatus(projectGuid, runGuid, opts) {
|
|
|
41798
42467
|
}
|
|
41799
42468
|
}
|
|
41800
42469
|
}
|
|
41801
|
-
await new Promise((
|
|
42470
|
+
await new Promise((resolve20) => setTimeout(resolve20, getPollInterval()));
|
|
41802
42471
|
}
|
|
41803
42472
|
}
|
|
41804
42473
|
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 () => {
|
|
@@ -41850,6 +42519,10 @@ var testCommand = new Command("test").description("Run tests").argument("[path]"
|
|
|
41850
42519
|
console.log(error(`Run failed: ${data.errorMessage}`));
|
|
41851
42520
|
console.log("");
|
|
41852
42521
|
}
|
|
42522
|
+
if (data.dbSummary) {
|
|
42523
|
+
console.log(muted(`Test db: ${data.dbSummary}`));
|
|
42524
|
+
console.log("");
|
|
42525
|
+
}
|
|
41853
42526
|
if (data.failed > 0) {
|
|
41854
42527
|
console.log(error("Failures:"));
|
|
41855
42528
|
for (const r of data.results.filter((r7) => r7.status === "failed")) {
|
|
@@ -42008,21 +42681,21 @@ var locationCommand = new Command("location").description("Show location").argum
|
|
|
42008
42681
|
}));
|
|
42009
42682
|
|
|
42010
42683
|
// src/commands/doctor.ts
|
|
42011
|
-
import { existsSync as
|
|
42012
|
-
import { join as
|
|
42013
|
-
import { homedir as
|
|
42684
|
+
import { existsSync as existsSync18, readFileSync as readFileSync20, statSync as statSync8 } from "fs";
|
|
42685
|
+
import { join as join20, resolve as resolve16 } from "path";
|
|
42686
|
+
import { homedir as homedir14 } from "os";
|
|
42014
42687
|
|
|
42015
42688
|
// src/updater/state.ts
|
|
42016
|
-
import { readFileSync as
|
|
42017
|
-
import { join as
|
|
42018
|
-
import { homedir as
|
|
42019
|
-
var GIPITY_DIR =
|
|
42020
|
-
var LOCAL_DIR =
|
|
42021
|
-
var LOCAL_PKG_DIR =
|
|
42022
|
-
var LOCAL_ENTRY =
|
|
42023
|
-
var STATE_FILE =
|
|
42024
|
-
var SETTINGS_FILE =
|
|
42025
|
-
var UPDATE_LOG =
|
|
42689
|
+
import { readFileSync as readFileSync19, writeFileSync as writeFileSync14, mkdirSync as mkdirSync14, existsSync as existsSync17 } from "fs";
|
|
42690
|
+
import { join as join19 } from "path";
|
|
42691
|
+
import { homedir as homedir13 } from "os";
|
|
42692
|
+
var GIPITY_DIR = join19(homedir13(), ".gipity");
|
|
42693
|
+
var LOCAL_DIR = join19(GIPITY_DIR, "local");
|
|
42694
|
+
var LOCAL_PKG_DIR = join19(LOCAL_DIR, "node_modules", "gipity");
|
|
42695
|
+
var LOCAL_ENTRY = join19(LOCAL_PKG_DIR, "dist", "index.js");
|
|
42696
|
+
var STATE_FILE = join19(GIPITY_DIR, "update-state.json");
|
|
42697
|
+
var SETTINGS_FILE = join19(GIPITY_DIR, "settings.json");
|
|
42698
|
+
var UPDATE_LOG = join19(GIPITY_DIR, "update.log");
|
|
42026
42699
|
var DEFAULT_STATE = {
|
|
42027
42700
|
installedVersion: null,
|
|
42028
42701
|
lastCheckAt: 0,
|
|
@@ -42033,24 +42706,24 @@ var DEFAULT_SETTINGS = {
|
|
|
42033
42706
|
autoUpdates: true
|
|
42034
42707
|
};
|
|
42035
42708
|
function ensureDir() {
|
|
42036
|
-
|
|
42709
|
+
mkdirSync14(GIPITY_DIR, { recursive: true });
|
|
42037
42710
|
}
|
|
42038
42711
|
function readState() {
|
|
42039
|
-
if (!
|
|
42712
|
+
if (!existsSync17(STATE_FILE)) return { ...DEFAULT_STATE };
|
|
42040
42713
|
try {
|
|
42041
|
-
return { ...DEFAULT_STATE, ...JSON.parse(
|
|
42714
|
+
return { ...DEFAULT_STATE, ...JSON.parse(readFileSync19(STATE_FILE, "utf-8")) };
|
|
42042
42715
|
} catch {
|
|
42043
42716
|
return { ...DEFAULT_STATE };
|
|
42044
42717
|
}
|
|
42045
42718
|
}
|
|
42046
42719
|
function writeState(state) {
|
|
42047
42720
|
ensureDir();
|
|
42048
|
-
|
|
42721
|
+
writeFileSync14(STATE_FILE, JSON.stringify(state, null, 2));
|
|
42049
42722
|
}
|
|
42050
42723
|
function readSettings() {
|
|
42051
|
-
if (!
|
|
42724
|
+
if (!existsSync17(SETTINGS_FILE)) return { ...DEFAULT_SETTINGS };
|
|
42052
42725
|
try {
|
|
42053
|
-
return { ...DEFAULT_SETTINGS, ...JSON.parse(
|
|
42726
|
+
return { ...DEFAULT_SETTINGS, ...JSON.parse(readFileSync19(SETTINGS_FILE, "utf-8")) };
|
|
42054
42727
|
} catch {
|
|
42055
42728
|
return { ...DEFAULT_SETTINGS };
|
|
42056
42729
|
}
|
|
@@ -42066,7 +42739,7 @@ function updatesDisabled() {
|
|
|
42066
42739
|
init_colors();
|
|
42067
42740
|
init_auth();
|
|
42068
42741
|
var NODE_MIN_MAJOR = 18;
|
|
42069
|
-
function versionManagerNode(execPath, env = process.env, home =
|
|
42742
|
+
function versionManagerNode(execPath, env = process.env, home = homedir14()) {
|
|
42070
42743
|
const norm = (s) => s.replace(/\\/g, "/").replace(/\/+$/, "") + "/";
|
|
42071
42744
|
const p = norm(execPath);
|
|
42072
42745
|
const h = norm(home);
|
|
@@ -42083,10 +42756,10 @@ function versionManagerNode(execPath, env = process.env, home = homedir13()) {
|
|
|
42083
42756
|
return null;
|
|
42084
42757
|
}
|
|
42085
42758
|
function localVersion() {
|
|
42086
|
-
const pkgPath =
|
|
42087
|
-
if (!
|
|
42759
|
+
const pkgPath = join20(LOCAL_PKG_DIR, "package.json");
|
|
42760
|
+
if (!existsSync18(pkgPath)) return null;
|
|
42088
42761
|
try {
|
|
42089
|
-
return JSON.parse(
|
|
42762
|
+
return JSON.parse(readFileSync20(pkgPath, "utf-8")).version ?? null;
|
|
42090
42763
|
} catch {
|
|
42091
42764
|
return null;
|
|
42092
42765
|
}
|
|
@@ -42094,7 +42767,7 @@ function localVersion() {
|
|
|
42094
42767
|
function shimVersion() {
|
|
42095
42768
|
try {
|
|
42096
42769
|
const url = new URL("../../package.json", import.meta.url);
|
|
42097
|
-
return JSON.parse(
|
|
42770
|
+
return JSON.parse(readFileSync20(url, "utf-8")).version;
|
|
42098
42771
|
} catch {
|
|
42099
42772
|
return "unknown";
|
|
42100
42773
|
}
|
|
@@ -42109,7 +42782,7 @@ function rel(t) {
|
|
|
42109
42782
|
}
|
|
42110
42783
|
function relayAutostartInstalled() {
|
|
42111
42784
|
try {
|
|
42112
|
-
return
|
|
42785
|
+
return existsSync18(planFor({ cliPath: resolveCliPath() }).path);
|
|
42113
42786
|
} catch (err) {
|
|
42114
42787
|
if (err instanceof UnsupportedPlatformError) return null;
|
|
42115
42788
|
return null;
|
|
@@ -42147,7 +42820,7 @@ function gatherEnv(opts = {}) {
|
|
|
42147
42820
|
const cli = {
|
|
42148
42821
|
shim_version: shimVersion(),
|
|
42149
42822
|
local_version: localVersion(),
|
|
42150
|
-
local_install_ok:
|
|
42823
|
+
local_install_ok: existsSync18(LOCAL_ENTRY),
|
|
42151
42824
|
auto_updates: !dis.disabled,
|
|
42152
42825
|
updates_disabled_reason: dis.disabled ? dis.reason ?? null : null,
|
|
42153
42826
|
last_check_at: updState.lastCheckAt,
|
|
@@ -42177,12 +42850,15 @@ var doctorCommand = new Command("doctor").description("Check install + environme
|
|
|
42177
42850
|
console.log(`${muted("claude code ")} installed ${yn(env.claude.installed)} \xB7 authenticated ${yn(env.claude.authenticated)}`);
|
|
42178
42851
|
const autostartLabel = env.relay.autostart === null ? muted("n/a") : yn(env.relay.autostart);
|
|
42179
42852
|
console.log(`${muted("relay ")} paired ${yn(env.relay.paired)} \xB7 running ${yn(env.relay.running)} \xB7 autostart ${autostartLabel}${env.relay.paused ? warning(" \xB7 paused") : ""}${env.relay.device ? muted(` (${env.relay.device.name})`) : ""}`);
|
|
42180
|
-
|
|
42853
|
+
if (existsSync18(resolve16(process.cwd(), ".codex", "hooks.json"))) {
|
|
42854
|
+
console.log(`${muted("codex hooks ")} ${warning("written")} - session capture needs a one-time approval: run /hooks inside Codex in this project`);
|
|
42855
|
+
}
|
|
42856
|
+
console.log(`${muted("ready ")} ${env.ready ? success("yes") : warning("no - run `gipity build` (or the desktop app) to finish setup")}`);
|
|
42181
42857
|
const state = readState();
|
|
42182
42858
|
const settings = readSettings();
|
|
42183
42859
|
const dis = updatesDisabled();
|
|
42184
42860
|
const local = localVersion();
|
|
42185
|
-
const localOk =
|
|
42861
|
+
const localOk = existsSync18(LOCAL_ENTRY);
|
|
42186
42862
|
console.log("");
|
|
42187
42863
|
console.log(bold("CLI install"));
|
|
42188
42864
|
console.log(`${muted("shim version ")} ${shimVersion()}`);
|
|
@@ -42190,17 +42866,17 @@ var doctorCommand = new Command("doctor").description("Check install + environme
|
|
|
42190
42866
|
console.log(`${muted("local install ")} ${LOCAL_PKG_DIR}`);
|
|
42191
42867
|
console.log("");
|
|
42192
42868
|
console.log(`${muted("auto-updates ")} ${dis.disabled ? warning(`disabled (${dis.reason})`) : success("enabled")}`);
|
|
42193
|
-
console.log(`${muted("settings file ")} ${
|
|
42869
|
+
console.log(`${muted("settings file ")} ${existsSync18(SETTINGS_FILE) ? SETTINGS_FILE : dim("(default)")} autoUpdates=${settings.autoUpdates}`);
|
|
42194
42870
|
console.log(`${muted("last check ")} ${rel(state.lastCheckAt)}`);
|
|
42195
42871
|
console.log(`${muted("last error ")} ${state.lastError ? error(state.lastError) : dim("none")}`);
|
|
42196
|
-
console.log(`${muted("state file ")} ${
|
|
42197
|
-
console.log(`${muted("update log ")} ${
|
|
42872
|
+
console.log(`${muted("state file ")} ${existsSync18(STATE_FILE) ? STATE_FILE : dim("(none yet)")}`);
|
|
42873
|
+
console.log(`${muted("update log ")} ${existsSync18(UPDATE_LOG) ? `${UPDATE_LOG} (${statSync8(UPDATE_LOG).size} bytes)` : dim("(none yet)")}`);
|
|
42198
42874
|
console.log("");
|
|
42199
42875
|
console.log(dim("Force an update with: gipity update"));
|
|
42200
42876
|
});
|
|
42201
42877
|
|
|
42202
42878
|
// src/updater/check.ts
|
|
42203
|
-
import { appendFileSync, existsSync as
|
|
42879
|
+
import { appendFileSync, existsSync as existsSync19 } from "fs";
|
|
42204
42880
|
init_platform();
|
|
42205
42881
|
var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
|
|
42206
42882
|
function log(line) {
|
|
@@ -42235,7 +42911,7 @@ function installVersion(version) {
|
|
|
42235
42911
|
stdio: "ignore"
|
|
42236
42912
|
});
|
|
42237
42913
|
if (res.error) log(`npm spawn failed: ${res.error.message}`);
|
|
42238
|
-
return res.status === 0 &&
|
|
42914
|
+
return res.status === 0 && existsSync19(LOCAL_ENTRY);
|
|
42239
42915
|
}
|
|
42240
42916
|
async function runCheck(opts = {}) {
|
|
42241
42917
|
const state = readState();
|
|
@@ -42309,17 +42985,17 @@ var updateCommand = new Command("update").description("Update the CLI").action(a
|
|
|
42309
42985
|
init_api();
|
|
42310
42986
|
init_utils();
|
|
42311
42987
|
init_colors();
|
|
42312
|
-
import { existsSync as
|
|
42988
|
+
import { existsSync as existsSync22, readFileSync as readFileSync23, unlinkSync as unlinkSync7 } from "fs";
|
|
42313
42989
|
import { spawn as spawn2 } from "child_process";
|
|
42314
42990
|
|
|
42315
42991
|
// src/relay/daemon.ts
|
|
42316
42992
|
init_platform();
|
|
42317
42993
|
init_config();
|
|
42318
|
-
import { appendFileSync as appendFileSync3, mkdirSync as
|
|
42994
|
+
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync16, existsSync as existsSync21, readFileSync as readFileSync22, writeFileSync as writeFileSync15, chmodSync as chmodSync3, closeSync as closeSync4, openSync as openSync4, unlinkSync as unlinkSync6 } from "fs";
|
|
42319
42995
|
import { stat, readFile } from "fs/promises";
|
|
42320
42996
|
import { createInterface as createInterface2 } from "readline";
|
|
42321
|
-
import { homedir as
|
|
42322
|
-
import { join as
|
|
42997
|
+
import { homedir as homedir16, hostname as hostname5, platform as osPlatform3, loadavg as loadavg2, freemem as freemem2, totalmem as totalmem2, cpus as cpus2 } from "os";
|
|
42998
|
+
import { join as join23 } from "path";
|
|
42323
42999
|
init_auth();
|
|
42324
43000
|
init_api();
|
|
42325
43001
|
|
|
@@ -43025,7 +43701,7 @@ var DeltaBatcher = class {
|
|
|
43025
43701
|
};
|
|
43026
43702
|
|
|
43027
43703
|
// src/relay/daemon.ts
|
|
43028
|
-
import { randomUUID } from "crypto";
|
|
43704
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
43029
43705
|
|
|
43030
43706
|
// src/relay/agent-token.ts
|
|
43031
43707
|
init_api();
|
|
@@ -43074,22 +43750,22 @@ async function revokeRelayAgentToken() {
|
|
|
43074
43750
|
// src/relay/diagnostics.ts
|
|
43075
43751
|
init_platform();
|
|
43076
43752
|
init_client_context();
|
|
43077
|
-
import { cpus, freemem, totalmem, loadavg, release, uptime, homedir as
|
|
43078
|
-
import { statfsSync, readdirSync as
|
|
43079
|
-
import { join as
|
|
43753
|
+
import { cpus, freemem, totalmem, loadavg, release, uptime, homedir as homedir15 } from "os";
|
|
43754
|
+
import { statfsSync, readdirSync as readdirSync9 } from "fs";
|
|
43755
|
+
import { join as join21 } from "path";
|
|
43080
43756
|
var PROBE_TIMEOUT_MS = 4e3;
|
|
43081
43757
|
function parseVersion(out) {
|
|
43082
43758
|
const m = out.match(/\d+\.\d+(?:\.\d+)?(?:[-.\w]*)?/);
|
|
43083
43759
|
return m ? m[0] : void 0;
|
|
43084
43760
|
}
|
|
43085
43761
|
function runProbe(cmd, args) {
|
|
43086
|
-
return new Promise((
|
|
43762
|
+
return new Promise((resolve20) => {
|
|
43087
43763
|
let out = "";
|
|
43088
43764
|
let settled = false;
|
|
43089
43765
|
const done = (s) => {
|
|
43090
43766
|
if (!settled) {
|
|
43091
43767
|
settled = true;
|
|
43092
|
-
|
|
43768
|
+
resolve20(s);
|
|
43093
43769
|
}
|
|
43094
43770
|
};
|
|
43095
43771
|
let child;
|
|
@@ -43143,12 +43819,12 @@ async function detectGpu() {
|
|
|
43143
43819
|
function diskUsage() {
|
|
43144
43820
|
try {
|
|
43145
43821
|
if (typeof statfsSync !== "function") return void 0;
|
|
43146
|
-
const st2 = statfsSync(
|
|
43822
|
+
const st2 = statfsSync(join21(homedir15(), "GipityProjects"), { bigint: false });
|
|
43147
43823
|
if (!st2 || !st2.bsize) return void 0;
|
|
43148
43824
|
return { total: st2.bsize * st2.blocks, free: st2.bsize * st2.bavail };
|
|
43149
43825
|
} catch {
|
|
43150
43826
|
try {
|
|
43151
|
-
const st2 = statfsSync(
|
|
43827
|
+
const st2 = statfsSync(homedir15(), { bigint: false });
|
|
43152
43828
|
return { total: st2.bsize * st2.blocks, free: st2.bsize * st2.bavail };
|
|
43153
43829
|
} catch {
|
|
43154
43830
|
return void 0;
|
|
@@ -43157,7 +43833,7 @@ function diskUsage() {
|
|
|
43157
43833
|
}
|
|
43158
43834
|
function localProjectCount() {
|
|
43159
43835
|
try {
|
|
43160
|
-
return
|
|
43836
|
+
return readdirSync9(join21(homedir15(), "GipityProjects"), { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).length;
|
|
43161
43837
|
} catch {
|
|
43162
43838
|
return void 0;
|
|
43163
43839
|
}
|
|
@@ -43170,15 +43846,17 @@ async function collectDiagnostics() {
|
|
|
43170
43846
|
return [];
|
|
43171
43847
|
}
|
|
43172
43848
|
})();
|
|
43173
|
-
const [claude, codex, cursor, gpu] = await Promise.all([
|
|
43849
|
+
const [claude, codex, grok, cursor, gpu] = await Promise.all([
|
|
43174
43850
|
probeVersion("claude"),
|
|
43175
43851
|
probeVersion("codex"),
|
|
43852
|
+
probeVersion("grok"),
|
|
43176
43853
|
probeVersion("cursor"),
|
|
43177
43854
|
detectGpu()
|
|
43178
43855
|
]);
|
|
43179
43856
|
const agents = {};
|
|
43180
43857
|
if (claude) agents.claude_code = claude;
|
|
43181
43858
|
if (codex) agents.codex = codex;
|
|
43859
|
+
if (grok) agents.grok = grok;
|
|
43182
43860
|
if (cursor) agents.cursor = cursor;
|
|
43183
43861
|
return {
|
|
43184
43862
|
collected_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -43286,8 +43964,8 @@ var SessionPool = class {
|
|
|
43286
43964
|
const done = new Promise((r) => {
|
|
43287
43965
|
markDone = r;
|
|
43288
43966
|
});
|
|
43289
|
-
return new Promise((
|
|
43290
|
-
const turn = { onMessage: p.onMessage, resolve:
|
|
43967
|
+
return new Promise((resolve20, reject) => {
|
|
43968
|
+
const turn = { onMessage: p.onMessage, resolve: resolve20, reject, interruptRequested: false, wasHot, done, markDone };
|
|
43291
43969
|
session.current = turn;
|
|
43292
43970
|
session.state = "running";
|
|
43293
43971
|
session.lastActivityAt = Date.now();
|
|
@@ -43464,7 +44142,7 @@ var SessionPool = class {
|
|
|
43464
44142
|
// src/relay/daemon.ts
|
|
43465
44143
|
init_config();
|
|
43466
44144
|
init_api();
|
|
43467
|
-
var RELAY_LOG_PATH =
|
|
44145
|
+
var RELAY_LOG_PATH = join23(homedir16(), ".gipity", "relay.log");
|
|
43468
44146
|
var HEARTBEAT_INTERVAL_MS = parseInt(process.env.GIPITY_RELAY_HEARTBEAT_MS || "60000", 10);
|
|
43469
44147
|
var DIAGNOSTICS_INTERVAL_MS = parseInt(process.env.GIPITY_RELAY_DIAGNOSTICS_MS || String(24 * 60 * 60 * 1e3), 10);
|
|
43470
44148
|
var LONG_POLL_TIMEOUT_MS = parseInt(process.env.GIPITY_RELAY_POLL_TIMEOUT_MS || "35000", 10);
|
|
@@ -43564,7 +44242,7 @@ function lockLogPerms(dir, file) {
|
|
|
43564
44242
|
chmodSync3(dir, 448);
|
|
43565
44243
|
} catch {
|
|
43566
44244
|
}
|
|
43567
|
-
if (!
|
|
44245
|
+
if (!existsSync21(file)) {
|
|
43568
44246
|
try {
|
|
43569
44247
|
closeSync4(openSync4(file, "a", 384));
|
|
43570
44248
|
} catch {
|
|
@@ -43581,8 +44259,8 @@ function log2(level, msg, extra) {
|
|
|
43581
44259
|
const pretty = `${C7.dim(hhmmss())} ${badge(level)} ${C7.bold(msg)}${formatExtra(extra)}`;
|
|
43582
44260
|
process.stderr.write(pretty + "\n");
|
|
43583
44261
|
try {
|
|
43584
|
-
const dir =
|
|
43585
|
-
|
|
44262
|
+
const dir = join23(homedir16(), ".gipity");
|
|
44263
|
+
mkdirSync16(dir, { recursive: true });
|
|
43586
44264
|
lockLogPerms(dir, RELAY_LOG_PATH);
|
|
43587
44265
|
const json = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level, msg, ...extra ?? {} });
|
|
43588
44266
|
appendFileSync3(RELAY_LOG_PATH, json + "\n");
|
|
@@ -43706,10 +44384,10 @@ async function heartbeatLoop(ctx) {
|
|
|
43706
44384
|
}
|
|
43707
44385
|
await Promise.race([
|
|
43708
44386
|
sleep4(HEARTBEAT_INTERVAL_MS, ctx.abort.signal),
|
|
43709
|
-
new Promise((
|
|
44387
|
+
new Promise((resolve20) => {
|
|
43710
44388
|
heartbeatPoke = () => {
|
|
43711
44389
|
heartbeatPoke = null;
|
|
43712
|
-
|
|
44390
|
+
resolve20();
|
|
43713
44391
|
};
|
|
43714
44392
|
})
|
|
43715
44393
|
]);
|
|
@@ -43753,9 +44431,9 @@ async function dispatchLoop(ctx, opts) {
|
|
|
43753
44431
|
while (inflight.size >= MAX_CONCURRENT_DISPATCHES && !ctx.abort.signal.aborted) {
|
|
43754
44432
|
await Promise.race([
|
|
43755
44433
|
...inflight,
|
|
43756
|
-
new Promise((
|
|
43757
|
-
if (ctx.abort.signal.aborted) return
|
|
43758
|
-
ctx.abort.signal.addEventListener("abort", () =>
|
|
44434
|
+
new Promise((resolve20) => {
|
|
44435
|
+
if (ctx.abort.signal.aborted) return resolve20();
|
|
44436
|
+
ctx.abort.signal.addEventListener("abort", () => resolve20(), { once: true });
|
|
43759
44437
|
})
|
|
43760
44438
|
]);
|
|
43761
44439
|
}
|
|
@@ -43982,7 +44660,7 @@ function isSafeSessionId(s) {
|
|
|
43982
44660
|
function transcriptPathFor(cwd, sessionId) {
|
|
43983
44661
|
if (!isSafeSessionId(sessionId)) return null;
|
|
43984
44662
|
const slug = cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
43985
|
-
return
|
|
44663
|
+
return join23(homedir16(), ".claude", "projects", slug, `${sessionId}.jsonl`);
|
|
43986
44664
|
}
|
|
43987
44665
|
function formatDuration(ms2) {
|
|
43988
44666
|
const totalSec = ms2 / 1e3;
|
|
@@ -44051,7 +44729,7 @@ async function handleDispatch(claimed) {
|
|
|
44051
44729
|
{ onWarn: (msg, meta) => log2("warn", msg, { id: d.short_guid, ...meta }) }
|
|
44052
44730
|
);
|
|
44053
44731
|
const pushSystem = (content) => {
|
|
44054
|
-
queue.push({ kind: "system", content, ts: (/* @__PURE__ */ new Date()).toISOString(), source_uuid:
|
|
44732
|
+
queue.push({ kind: "system", content, ts: (/* @__PURE__ */ new Date()).toISOString(), source_uuid: randomUUID2() });
|
|
44055
44733
|
};
|
|
44056
44734
|
const flushQueue = async () => {
|
|
44057
44735
|
await queue.close(3e4);
|
|
@@ -44097,11 +44775,16 @@ async function handleDispatch(claimed) {
|
|
|
44097
44775
|
}
|
|
44098
44776
|
if (!syncFailed) pushSystem(bootstrapped ? "Project files synced." : "Attached files synced.");
|
|
44099
44777
|
}
|
|
44778
|
+
const adapter = getAdapterBySource(d.remote_type ?? "claude_code");
|
|
44779
|
+
if (adapter.key !== "claude") {
|
|
44780
|
+
await handleNonClaudeDispatch(adapter, d, cwd, queue, pushSystem, flushQueue);
|
|
44781
|
+
return;
|
|
44782
|
+
}
|
|
44100
44783
|
if (SESSION_POOL_ENABLED) {
|
|
44101
44784
|
const handled = await tryHandleViaPool(d, cwd, queue, pushSystem, flushQueue);
|
|
44102
44785
|
if (handled) return;
|
|
44103
44786
|
}
|
|
44104
|
-
const args = ["claude", "-p", d.message, "--permission-mode", "bypassPermissions"];
|
|
44787
|
+
const args = ["build", "--agent", "claude", "-p", d.message, "--permission-mode", "bypassPermissions"];
|
|
44105
44788
|
args.push("--append-system-prompt", GIPITY_QUESTION_PROTOCOL);
|
|
44106
44789
|
if (d.model) {
|
|
44107
44790
|
args.push("--model", d.model);
|
|
@@ -44109,7 +44792,7 @@ async function handleDispatch(claimed) {
|
|
|
44109
44792
|
if (d.kind === "resume" && d.remote_session_id) {
|
|
44110
44793
|
args.push("--resume", d.remote_session_id);
|
|
44111
44794
|
}
|
|
44112
|
-
log2("debug", "spawning gipity claude", {
|
|
44795
|
+
log2("debug", "spawning gipity build --agent claude", {
|
|
44113
44796
|
id: d.short_guid,
|
|
44114
44797
|
cwd,
|
|
44115
44798
|
args,
|
|
@@ -44158,8 +44841,8 @@ async function handleDispatch(claimed) {
|
|
|
44158
44841
|
const header = `Running Claude Code - ${counts.join(" + ")} words${resumeNote}`;
|
|
44159
44842
|
const ts2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
44160
44843
|
queue.push(
|
|
44161
|
-
{ kind: "prompt", prompt: d.message, ts: ts2, source_uuid:
|
|
44162
|
-
{ kind: "system", content: header, ts: ts2, source_uuid:
|
|
44844
|
+
{ kind: "prompt", prompt: d.message, ts: ts2, source_uuid: randomUUID2() },
|
|
44845
|
+
{ kind: "system", content: header, ts: ts2, source_uuid: randomUUID2() }
|
|
44163
44846
|
);
|
|
44164
44847
|
const t0 = Date.now();
|
|
44165
44848
|
let exitCode = 1;
|
|
@@ -44206,7 +44889,61 @@ async function handleDispatch(claimed) {
|
|
|
44206
44889
|
await ack(d.short_guid, "done", void 0, metrics);
|
|
44207
44890
|
} else {
|
|
44208
44891
|
log2("warn", "dispatch child exited nonzero", { id: d.short_guid, exitCode, ms: ms2 });
|
|
44209
|
-
await ack(d.short_guid, "error", `
|
|
44892
|
+
await ack(d.short_guid, "error", `Claude Code exited with code ${exitCode}${stderrNote}`);
|
|
44893
|
+
}
|
|
44894
|
+
}
|
|
44895
|
+
async function handleNonClaudeDispatch(adapter, d, cwd, queue, pushSystem, flushQueue) {
|
|
44896
|
+
const args = ["build", "--agent", adapter.key, "-p", d.message, "--bypass-approvals"];
|
|
44897
|
+
if (d.model) args.push("--model", d.model);
|
|
44898
|
+
if (d.kind === "resume" && d.remote_session_id) args.push("--resume", d.remote_session_id);
|
|
44899
|
+
log2("debug", `spawning gipity build --agent ${adapter.key}`, {
|
|
44900
|
+
id: d.short_guid,
|
|
44901
|
+
cwd,
|
|
44902
|
+
args,
|
|
44903
|
+
conv: d.conversation_guid,
|
|
44904
|
+
chain: d.kind === "resume" ? `resume ${d.remote_session_id}` : "start (fresh session)"
|
|
44905
|
+
});
|
|
44906
|
+
const words = d.message.trim().split(/\s+/).filter(Boolean).length;
|
|
44907
|
+
pushSystem(`Running ${adapter.displayName} - ${words.toLocaleString("en-US")} words`);
|
|
44908
|
+
const t0 = Date.now();
|
|
44909
|
+
let exitCode = 1;
|
|
44910
|
+
let spawnErr = null;
|
|
44911
|
+
let killed = false;
|
|
44912
|
+
let runtimeLimit = false;
|
|
44913
|
+
let stderrTail = "";
|
|
44914
|
+
try {
|
|
44915
|
+
const result = await spawnGipityClaude(args, cwd, d, queue, { streamJson: false });
|
|
44916
|
+
exitCode = result.exitCode;
|
|
44917
|
+
killed = result.killed;
|
|
44918
|
+
runtimeLimit = result.runtimeLimit ?? false;
|
|
44919
|
+
stderrTail = result.stderrTail ?? "";
|
|
44920
|
+
} catch (err) {
|
|
44921
|
+
spawnErr = err?.message || String(err);
|
|
44922
|
+
log2("error", "dispatch spawn failed", { id: d.short_guid, err: spawnErr });
|
|
44923
|
+
}
|
|
44924
|
+
const dur = formatDuration(Date.now() - t0);
|
|
44925
|
+
if (!spawnErr && !killed) {
|
|
44926
|
+
try {
|
|
44927
|
+
await spawnSync2(cwd, PROJECT_SYNC_TIMEOUT_MS);
|
|
44928
|
+
} catch (err) {
|
|
44929
|
+
log2("warn", "sync after dispatch failed", { id: d.short_guid, err: err?.message });
|
|
44930
|
+
}
|
|
44931
|
+
}
|
|
44932
|
+
const stderrNote = stderrTail ? `: ${stderrTail.slice(0, 300)}` : "";
|
|
44933
|
+
const tail = runtimeLimit ? `stopped after ${dur} (runtime limit)` : killed ? `cancelled (${dur})` : spawnErr ? `failed (${dur}: ${spawnErr})` : exitCode === 0 ? `finished (${dur})` : `failed (${dur}, exit ${exitCode}${stderrNote})`;
|
|
44934
|
+
pushSystem(`${adapter.displayName} ${tail}`);
|
|
44935
|
+
await flushQueue();
|
|
44936
|
+
if (runtimeLimit) {
|
|
44937
|
+
await ack(d.short_guid, "error", `${adapter.displayName} stopped after ${dur} (runtime limit)`);
|
|
44938
|
+
} else if (killed) {
|
|
44939
|
+
await ack(d.short_guid, "cancelled");
|
|
44940
|
+
} else if (spawnErr) {
|
|
44941
|
+
await ack(d.short_guid, "error", spawnErr);
|
|
44942
|
+
} else if (exitCode === 0) {
|
|
44943
|
+
log2("info", "dispatch done", { id: d.short_guid, agent: adapter.key });
|
|
44944
|
+
await ack(d.short_guid, "done");
|
|
44945
|
+
} else {
|
|
44946
|
+
await ack(d.short_guid, "error", `${adapter.displayName} exited with code ${exitCode}${stderrNote}`);
|
|
44210
44947
|
}
|
|
44211
44948
|
}
|
|
44212
44949
|
async function wrapPoolMessage(d, cwd, resume) {
|
|
@@ -44248,8 +44985,8 @@ async function tryHandleViaPool(d, cwd, queue, pushSystem, flushQueue) {
|
|
|
44248
44985
|
const words = d.message.trim().split(/\s+/).filter(Boolean).length;
|
|
44249
44986
|
const ts0 = (/* @__PURE__ */ new Date()).toISOString();
|
|
44250
44987
|
queue.push(
|
|
44251
|
-
{ kind: "prompt", prompt: d.message, ts: ts0, source_uuid:
|
|
44252
|
-
{ kind: "system", content: `Running Claude Code - ${words.toLocaleString("en-US")} words${wasLive ? " (hot session)" : ""}`, ts: ts0, source_uuid:
|
|
44988
|
+
{ kind: "prompt", prompt: d.message, ts: ts0, source_uuid: randomUUID2() },
|
|
44989
|
+
{ kind: "system", content: `Running Claude Code - ${words.toLocaleString("en-US")} words${wasLive ? " (hot session)" : ""}`, ts: ts0, source_uuid: randomUUID2() }
|
|
44253
44990
|
);
|
|
44254
44991
|
const phases = new PhaseTracker();
|
|
44255
44992
|
const deltaAcc = new DeltaAccumulator(getRelaySecrets);
|
|
@@ -44304,7 +45041,7 @@ async function tryHandleViaPool(d, cwd, queue, pushSystem, flushQueue) {
|
|
|
44304
45041
|
const ts2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
44305
45042
|
for (const e of entries) {
|
|
44306
45043
|
e.ts = ts2;
|
|
44307
|
-
if (!e.source_uuid) e.source_uuid =
|
|
45044
|
+
if (!e.source_uuid) e.source_uuid = randomUUID2();
|
|
44308
45045
|
}
|
|
44309
45046
|
queue.push(...entries);
|
|
44310
45047
|
};
|
|
@@ -44377,11 +45114,11 @@ async function resolveCwdForProject(d) {
|
|
|
44377
45114
|
throw new Error(`Invalid project slug: ${JSON.stringify(d.project_slug)}`);
|
|
44378
45115
|
}
|
|
44379
45116
|
const root = getProjectsRoot();
|
|
44380
|
-
const path5 =
|
|
44381
|
-
const configPath =
|
|
44382
|
-
if (
|
|
45117
|
+
const path5 = join23(root, d.project_slug);
|
|
45118
|
+
const configPath = join23(path5, ".gipity.json");
|
|
45119
|
+
if (existsSync21(configPath)) {
|
|
44383
45120
|
try {
|
|
44384
|
-
const cfg = JSON.parse(
|
|
45121
|
+
const cfg = JSON.parse(readFileSync22(configPath, "utf-8"));
|
|
44385
45122
|
if (cfg.projectGuid === d.project_guid) return { cwd: path5, bootstrapped: false };
|
|
44386
45123
|
log2("warn", "project dir exists but guid mismatch - using it anyway", {
|
|
44387
45124
|
path: path5,
|
|
@@ -44393,9 +45130,9 @@ async function resolveCwdForProject(d) {
|
|
|
44393
45130
|
}
|
|
44394
45131
|
}
|
|
44395
45132
|
log2("info", "bootstrapping new project dir", { slug: d.project_slug, path: path5 });
|
|
44396
|
-
|
|
45133
|
+
mkdirSync16(path5, { recursive: true });
|
|
44397
45134
|
const apiBase2 = getApiBaseOverride() || DEFAULT_API_BASE;
|
|
44398
|
-
|
|
45135
|
+
writeFileSync15(configPath, JSON.stringify({
|
|
44399
45136
|
projectGuid: d.project_guid,
|
|
44400
45137
|
projectSlug: d.project_slug,
|
|
44401
45138
|
accountSlug: d.account_slug,
|
|
@@ -44407,10 +45144,7 @@ async function resolveCwdForProject(d) {
|
|
|
44407
45144
|
const origCwd = process.cwd();
|
|
44408
45145
|
try {
|
|
44409
45146
|
process.chdir(path5);
|
|
44410
|
-
|
|
44411
|
-
setupClaudeMd();
|
|
44412
|
-
setupAgentsMd();
|
|
44413
|
-
setupGitignore();
|
|
45147
|
+
setupProjectTools();
|
|
44414
45148
|
} finally {
|
|
44415
45149
|
process.chdir(origCwd);
|
|
44416
45150
|
}
|
|
@@ -44475,7 +45209,7 @@ async function killRunningForConv(convGuid) {
|
|
|
44475
45209
|
}
|
|
44476
45210
|
}
|
|
44477
45211
|
const graceTimers = [];
|
|
44478
|
-
const escalate = new Promise((
|
|
45212
|
+
const escalate = new Promise((resolve20) => {
|
|
44479
45213
|
const t = setTimeout(() => {
|
|
44480
45214
|
for (const e of matches) {
|
|
44481
45215
|
log2("warn", "previous dispatch ignored SIGTERM - escalating to SIGKILL", { conv: convGuid });
|
|
@@ -44484,7 +45218,7 @@ async function killRunningForConv(convGuid) {
|
|
|
44484
45218
|
} catch {
|
|
44485
45219
|
}
|
|
44486
45220
|
}
|
|
44487
|
-
|
|
45221
|
+
resolve20();
|
|
44488
45222
|
}, KILL_GRACE_MS);
|
|
44489
45223
|
graceTimers.push(t);
|
|
44490
45224
|
});
|
|
@@ -44495,7 +45229,7 @@ async function killRunningForConv(convGuid) {
|
|
|
44495
45229
|
}
|
|
44496
45230
|
async function spawnSync2(cwd, timeoutMs, onSpawn) {
|
|
44497
45231
|
const cmd = process.env.GIPITY_RELAY_CLAUDE_CMD || resolveCommand("gipity");
|
|
44498
|
-
return new Promise((
|
|
45232
|
+
return new Promise((resolve20, reject) => {
|
|
44499
45233
|
const child = spawnCommand(cmd, ["sync", "--json"], {
|
|
44500
45234
|
cwd,
|
|
44501
45235
|
env: childEnv(),
|
|
@@ -44519,7 +45253,7 @@ async function spawnSync2(cwd, timeoutMs, onSpawn) {
|
|
|
44519
45253
|
} catch {
|
|
44520
45254
|
}
|
|
44521
45255
|
try {
|
|
44522
|
-
unlinkSync6(
|
|
45256
|
+
unlinkSync6(join23(cwd, ".gipity", "sync.lock"));
|
|
44523
45257
|
} catch {
|
|
44524
45258
|
}
|
|
44525
45259
|
finish(() => reject(new Error(`timed out after ${Math.round(timeoutMs / 1e3)}s`)));
|
|
@@ -44535,7 +45269,7 @@ async function spawnSync2(cwd, timeoutMs, onSpawn) {
|
|
|
44535
45269
|
child.on("exit", (code) => finish(() => {
|
|
44536
45270
|
if (code === 0) {
|
|
44537
45271
|
log2("info", "sync done", { cwd, stdoutLen });
|
|
44538
|
-
|
|
45272
|
+
resolve20();
|
|
44539
45273
|
} else {
|
|
44540
45274
|
reject(new Error(`gipity sync exited ${code}${stderrBuf ? `: ${stderrBuf.trim().slice(0, 300)}` : ""}`));
|
|
44541
45275
|
}
|
|
@@ -44543,10 +45277,11 @@ async function spawnSync2(cwd, timeoutMs, onSpawn) {
|
|
|
44543
45277
|
});
|
|
44544
45278
|
}
|
|
44545
45279
|
async function spawnGipityClaude(args, cwd, d, queue, meta) {
|
|
45280
|
+
const streamJson = meta?.streamJson !== false;
|
|
44546
45281
|
const cmd = process.env.GIPITY_RELAY_CLAUDE_CMD || resolveCommand("gipity");
|
|
44547
|
-
const fullArgs = [...args, "--output-format", "stream-json", "--verbose", "--include-partial-messages"];
|
|
44548
|
-
const env = childEnv({ GIPITY_CONVERSATION_GUID: d.conversation_guid, GIPITY_CAPTURE: "off" });
|
|
44549
|
-
return new Promise((
|
|
45282
|
+
const fullArgs = streamJson ? [...args, "--output-format", "stream-json", "--verbose", "--include-partial-messages"] : [...args];
|
|
45283
|
+
const env = streamJson ? childEnv({ GIPITY_CONVERSATION_GUID: d.conversation_guid, GIPITY_CAPTURE: "off" }) : childEnv({ GIPITY_CONVERSATION_GUID: d.conversation_guid });
|
|
45284
|
+
return new Promise((resolve20, reject) => {
|
|
44550
45285
|
const child = spawnCommand(cmd, fullArgs, { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
44551
45286
|
let resolveExited = () => {
|
|
44552
45287
|
};
|
|
@@ -44675,16 +45410,18 @@ async function spawnGipityClaude(args, cwd, d, queue, meta) {
|
|
|
44675
45410
|
const ts2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
44676
45411
|
for (const e of entries) {
|
|
44677
45412
|
e.ts = ts2;
|
|
44678
|
-
if (!e.source_uuid) e.source_uuid =
|
|
45413
|
+
if (!e.source_uuid) e.source_uuid = randomUUID2();
|
|
44679
45414
|
}
|
|
44680
45415
|
q.push(...entries);
|
|
44681
45416
|
});
|
|
44682
45417
|
child.stdout?.on("data", (chunk) => {
|
|
44683
45418
|
stdoutBytesTotal += chunk.length;
|
|
44684
45419
|
lastStdoutByteAt = Date.now();
|
|
44685
|
-
splitter.push(chunk);
|
|
45420
|
+
if (streamJson) splitter.push(chunk);
|
|
45421
|
+
});
|
|
45422
|
+
child.stdout?.on("end", () => {
|
|
45423
|
+
if (streamJson) splitter.flush();
|
|
44686
45424
|
});
|
|
44687
|
-
child.stdout?.on("end", () => splitter.flush());
|
|
44688
45425
|
const errPrefix = C7.dim("\u2502 ");
|
|
44689
45426
|
const stderrTail = [];
|
|
44690
45427
|
const errRl = child.stderr ? createInterface2({ input: child.stderr }) : null;
|
|
@@ -44710,7 +45447,7 @@ async function spawnGipityClaude(args, cwd, d, queue, meta) {
|
|
|
44710
45447
|
if (signal === "SIGTERM" || signal === "SIGKILL") killed = true;
|
|
44711
45448
|
if (finalResult) {
|
|
44712
45449
|
finalResult.ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
44713
|
-
finalResult.source_uuid =
|
|
45450
|
+
finalResult.source_uuid = randomUUID2();
|
|
44714
45451
|
q.push(finalResult);
|
|
44715
45452
|
}
|
|
44716
45453
|
if (unmapped.size > 0) {
|
|
@@ -44724,7 +45461,7 @@ async function spawnGipityClaude(args, cwd, d, queue, meta) {
|
|
|
44724
45461
|
void postProgress(d.conversation_guid, buildProgressPayload(false));
|
|
44725
45462
|
if (ownQueue) await q.close();
|
|
44726
45463
|
cleanup();
|
|
44727
|
-
|
|
45464
|
+
resolve20({
|
|
44728
45465
|
exitCode: code ?? 1,
|
|
44729
45466
|
killed,
|
|
44730
45467
|
runtimeLimit,
|
|
@@ -44738,10 +45475,10 @@ async function runDispatchSync(d, cwd) {
|
|
|
44738
45475
|
let killed = false;
|
|
44739
45476
|
try {
|
|
44740
45477
|
await spawnSync2(cwd, PROJECT_SYNC_TIMEOUT_MS, (child) => {
|
|
44741
|
-
const exited = new Promise((
|
|
45478
|
+
const exited = new Promise((resolve20) => {
|
|
44742
45479
|
child.once("exit", (_code, signal) => {
|
|
44743
45480
|
if (signal === "SIGTERM") killed = true;
|
|
44744
|
-
|
|
45481
|
+
resolve20();
|
|
44745
45482
|
});
|
|
44746
45483
|
});
|
|
44747
45484
|
running.set(d.short_guid, { child, convGuid: d.conversation_guid, exited });
|
|
@@ -44772,18 +45509,18 @@ function killDispatch(shortGuid) {
|
|
|
44772
45509
|
return false;
|
|
44773
45510
|
}
|
|
44774
45511
|
function sleep4(ms2, signal) {
|
|
44775
|
-
return new Promise((
|
|
44776
|
-
if (signal?.aborted) return
|
|
45512
|
+
return new Promise((resolve20) => {
|
|
45513
|
+
if (signal?.aborted) return resolve20();
|
|
44777
45514
|
let onAbort = null;
|
|
44778
45515
|
const t = setTimeout(() => {
|
|
44779
45516
|
if (onAbort && signal) signal.removeEventListener("abort", onAbort);
|
|
44780
|
-
|
|
45517
|
+
resolve20();
|
|
44781
45518
|
}, ms2);
|
|
44782
45519
|
if (signal) {
|
|
44783
45520
|
onAbort = () => {
|
|
44784
45521
|
clearTimeout(t);
|
|
44785
45522
|
signal.removeEventListener("abort", onAbort);
|
|
44786
|
-
|
|
45523
|
+
resolve20();
|
|
44787
45524
|
};
|
|
44788
45525
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
44789
45526
|
}
|
|
@@ -44796,7 +45533,7 @@ init_colors();
|
|
|
44796
45533
|
function requirePaired() {
|
|
44797
45534
|
const device = getDevice();
|
|
44798
45535
|
if (!device) {
|
|
44799
|
-
console.error(`${error("No paired device.")} Run ${bold("gipity
|
|
45536
|
+
console.error(`${error("No paired device.")} Run ${bold("gipity connect")} to pair this machine.`);
|
|
44800
45537
|
process.exit(1);
|
|
44801
45538
|
}
|
|
44802
45539
|
return device;
|
|
@@ -44869,7 +45606,7 @@ function registerInstallCommands(relayCommand2) {
|
|
|
44869
45606
|
|
|
44870
45607
|
// src/commands/relay.ts
|
|
44871
45608
|
var relayCommand = new Command("relay").description("Pair with the web CLI");
|
|
44872
|
-
relayCommand.command("setup").description("Pair this machine and start the relay - non-interactive (for installers/GUIs)").option("--name <name>", "Device name shown in the web CLI (default: this machine's hostname)").option("--no-start", "Pair only; do not start the relay daemon now").option("--no-autostart", "Skip the OS login service (use when a supervising app owns the daemon)").option("--force", "Re-pair even if already paired (
|
|
45609
|
+
relayCommand.command("setup").description("Pair this machine and start the relay - non-interactive (for installers/GUIs)").option("--name <name>", "Device name shown in the web CLI (default: this machine's hostname)").option("--no-start", "Pair only; do not start the relay daemon now").option("--no-autostart", "Skip the OS login service (use when a supervising app owns the daemon)").option("--force", "Re-pair even if already paired (re-registers this machine, rotating its token)").option("--json", "Machine-readable output").action(async (opts) => {
|
|
44873
45610
|
const fail = (code, message) => {
|
|
44874
45611
|
if (opts.json) {
|
|
44875
45612
|
console.log(JSON.stringify({ ok: false, code, error: message }));
|
|
@@ -44937,7 +45674,7 @@ relayCommand.command("status").description("Show pairing status").option("--json
|
|
|
44937
45674
|
return;
|
|
44938
45675
|
}
|
|
44939
45676
|
if (!s.device) {
|
|
44940
|
-
console.log(`${muted("No paired device.")} Run ${brand("gipity
|
|
45677
|
+
console.log(`${muted("No paired device.")} Run ${brand("gipity connect")} to pair this machine.`);
|
|
44941
45678
|
return;
|
|
44942
45679
|
}
|
|
44943
45680
|
console.log(`${bold("Device:")} ${brand(s.device.name)} ${muted(`(${s.device.guid})`)}`);
|
|
@@ -44953,11 +45690,11 @@ relayCommand.command("run").description("Run the background service").option("-v
|
|
|
44953
45690
|
});
|
|
44954
45691
|
relayCommand.command("stop").description("Stop the background service").option("--force", "Force-stop if it doesn't exit cleanly within 5s").action(async (opts) => {
|
|
44955
45692
|
const pidPath = getDaemonPidPath();
|
|
44956
|
-
if (!
|
|
45693
|
+
if (!existsSync22(pidPath)) {
|
|
44957
45694
|
console.log(muted("Background service isn't running."));
|
|
44958
45695
|
return;
|
|
44959
45696
|
}
|
|
44960
|
-
const pid = parseInt(
|
|
45697
|
+
const pid = parseInt(readFileSync23(pidPath, "utf-8").trim(), 10);
|
|
44961
45698
|
if (!pid || isNaN(pid)) {
|
|
44962
45699
|
console.error(error("PID file is empty or malformed."));
|
|
44963
45700
|
process.exit(1);
|
|
@@ -45081,8 +45818,8 @@ relayCommand.command("revoke").description("Revoke and forget this device").acti
|
|
|
45081
45818
|
} catch {
|
|
45082
45819
|
}
|
|
45083
45820
|
const pidPath = getDaemonPidPath();
|
|
45084
|
-
if (
|
|
45085
|
-
const pid = parseInt(
|
|
45821
|
+
if (existsSync22(pidPath)) {
|
|
45822
|
+
const pid = parseInt(readFileSync23(pidPath, "utf-8").trim(), 10);
|
|
45086
45823
|
if (pid && !isNaN(pid)) {
|
|
45087
45824
|
try {
|
|
45088
45825
|
process.kill(pid, "SIGTERM");
|
|
@@ -45095,13 +45832,13 @@ relayCommand.command("revoke").description("Revoke and forget this device").acti
|
|
|
45095
45832
|
});
|
|
45096
45833
|
relayCommand.command("log").description("Tail the service log").option("-n, --lines <n>", "How many lines to print (default 100)", "100").option("-f, --follow", "Follow the log like `tail -f`").action((opts) => {
|
|
45097
45834
|
const path5 = RELAY_LOG_PATH;
|
|
45098
|
-
if (!
|
|
45835
|
+
if (!existsSync22(path5)) {
|
|
45099
45836
|
console.log(muted("No log file yet. Start the service with `gipity relay run` (or install it)."));
|
|
45100
45837
|
return;
|
|
45101
45838
|
}
|
|
45102
45839
|
const lines = parseInt(opts.lines, 10) || 100;
|
|
45103
45840
|
try {
|
|
45104
|
-
const all =
|
|
45841
|
+
const all = readFileSync23(path5, "utf-8").split("\n");
|
|
45105
45842
|
const tail = all.slice(-lines - 1).join("\n");
|
|
45106
45843
|
process.stdout.write(tail);
|
|
45107
45844
|
} catch (err) {
|
|
@@ -45127,7 +45864,7 @@ registerInstallCommands(relayCommand);
|
|
|
45127
45864
|
function requirePaired2() {
|
|
45128
45865
|
const device = getDevice();
|
|
45129
45866
|
if (!device) {
|
|
45130
|
-
console.error(`${error("No paired device.")} Run ${brand("gipity
|
|
45867
|
+
console.error(`${error("No paired device.")} Run ${brand("gipity connect")} to pair this machine.`);
|
|
45131
45868
|
process.exit(1);
|
|
45132
45869
|
}
|
|
45133
45870
|
return device;
|
|
@@ -45136,9 +45873,9 @@ function requirePaired2() {
|
|
|
45136
45873
|
// src/commands/setup.ts
|
|
45137
45874
|
init_auth();
|
|
45138
45875
|
init_colors();
|
|
45139
|
-
var
|
|
45876
|
+
var connectCommand = new Command("connect").alias("setup").description("Connect this computer to gipity.ai so the web CLI can drive it (no project, no launch)").action(async () => {
|
|
45140
45877
|
try {
|
|
45141
|
-
console.log(` ${bold("Gipity
|
|
45878
|
+
console.log(` ${bold("Gipity connect")} ${muted("- let gipity.ai drive this computer")}`);
|
|
45142
45879
|
console.log("");
|
|
45143
45880
|
let auth = getAuth();
|
|
45144
45881
|
if (auth && !sessionExpired()) {
|
|
@@ -45156,9 +45893,9 @@ var setupCommand = new Command("setup").description("Set up this computer as a r
|
|
|
45156
45893
|
if (enabled) {
|
|
45157
45894
|
const running2 = isRelayEnabled() && !isPaused();
|
|
45158
45895
|
console.log(` ${success("Done")} \u2014 your relay ${running2 ? "is running in the background" : "is set up"} and will start with your computer.`);
|
|
45159
|
-
console.log(` ${muted("Open")} ${brand("gipity.ai")} ${muted("and start a chat to drive
|
|
45896
|
+
console.log(` ${muted("Open")} ${brand("gipity.ai")} ${muted("and start a chat to drive your coding agent here. Manage it with `gipity relay status`.")}`);
|
|
45160
45897
|
} else {
|
|
45161
|
-
console.log(` ${muted("No relay set up. Run `gipity
|
|
45898
|
+
console.log(` ${muted("No relay set up. Run `gipity connect` again anytime, or `gipity build` to start building.")}`);
|
|
45162
45899
|
}
|
|
45163
45900
|
console.log("");
|
|
45164
45901
|
} catch (err) {
|
|
@@ -45174,15 +45911,15 @@ init_api();
|
|
|
45174
45911
|
init_auth();
|
|
45175
45912
|
init_utils();
|
|
45176
45913
|
init_colors();
|
|
45177
|
-
import { existsSync as
|
|
45178
|
-
import { homedir as
|
|
45179
|
-
import { join as
|
|
45914
|
+
import { existsSync as existsSync23, rmSync as rmSync3, unlinkSync as unlinkSync8, readFileSync as readFileSync24, writeFileSync as writeFileSync16 } from "fs";
|
|
45915
|
+
import { homedir as homedir17, platform as osPlatform4 } from "os";
|
|
45916
|
+
import { join as join24, resolve as resolve18 } from "path";
|
|
45180
45917
|
function removeGipityPluginConfig() {
|
|
45181
|
-
const settingsPath =
|
|
45182
|
-
if (!
|
|
45918
|
+
const settingsPath = join24(homedir17(), ".claude", "settings.json");
|
|
45919
|
+
if (!existsSync23(settingsPath)) return false;
|
|
45183
45920
|
let settings;
|
|
45184
45921
|
try {
|
|
45185
|
-
settings = JSON.parse(
|
|
45922
|
+
settings = JSON.parse(readFileSync24(settingsPath, "utf-8"));
|
|
45186
45923
|
} catch {
|
|
45187
45924
|
return false;
|
|
45188
45925
|
}
|
|
@@ -45197,7 +45934,7 @@ function removeGipityPluginConfig() {
|
|
|
45197
45934
|
if (Object.keys(settings.extraKnownMarketplaces).length === 0) delete settings.extraKnownMarketplaces;
|
|
45198
45935
|
changed = true;
|
|
45199
45936
|
}
|
|
45200
|
-
if (changed)
|
|
45937
|
+
if (changed) writeFileSync16(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
45201
45938
|
return changed;
|
|
45202
45939
|
}
|
|
45203
45940
|
function removeInstallerPathLines() {
|
|
@@ -45205,11 +45942,11 @@ function removeInstallerPathLines() {
|
|
|
45205
45942
|
const marker = "# Added by the Gipity installer";
|
|
45206
45943
|
const touched = [];
|
|
45207
45944
|
for (const name of [".bashrc", ".zshrc", ".profile"]) {
|
|
45208
|
-
const rc2 =
|
|
45209
|
-
if (!
|
|
45945
|
+
const rc2 = join24(homedir17(), name);
|
|
45946
|
+
if (!existsSync23(rc2)) continue;
|
|
45210
45947
|
let text;
|
|
45211
45948
|
try {
|
|
45212
|
-
text =
|
|
45949
|
+
text = readFileSync24(rc2, "utf-8");
|
|
45213
45950
|
} catch {
|
|
45214
45951
|
continue;
|
|
45215
45952
|
}
|
|
@@ -45219,7 +45956,7 @@ function removeInstallerPathLines() {
|
|
|
45219
45956
|
);
|
|
45220
45957
|
if (kept.length === lines.length) continue;
|
|
45221
45958
|
try {
|
|
45222
|
-
|
|
45959
|
+
writeFileSync16(rc2, kept.join("\n"));
|
|
45223
45960
|
touched.push(rc2);
|
|
45224
45961
|
} catch {
|
|
45225
45962
|
}
|
|
@@ -45227,11 +45964,11 @@ function removeInstallerPathLines() {
|
|
|
45227
45964
|
return touched;
|
|
45228
45965
|
}
|
|
45229
45966
|
function tildify2(p) {
|
|
45230
|
-
const home =
|
|
45967
|
+
const home = homedir17();
|
|
45231
45968
|
return p === home || p.startsWith(home + "/") ? "~" + p.slice(home.length) : p;
|
|
45232
45969
|
}
|
|
45233
45970
|
function resolveCliPath2() {
|
|
45234
|
-
return
|
|
45971
|
+
return resolve18(process.argv[1] ?? "gipity");
|
|
45235
45972
|
}
|
|
45236
45973
|
async function stopDaemon() {
|
|
45237
45974
|
if (!isDaemonRunning()) return;
|
|
@@ -45265,7 +46002,7 @@ function removeServiceUnit() {
|
|
|
45265
46002
|
const r = spawnSyncCommand(argv2[0], argv2.slice(1), { stdio: "ignore" });
|
|
45266
46003
|
if (r.status !== 0) allOk = false;
|
|
45267
46004
|
}
|
|
45268
|
-
if (
|
|
46005
|
+
if (existsSync23(plan2.path)) {
|
|
45269
46006
|
try {
|
|
45270
46007
|
unlinkSync8(plan2.path);
|
|
45271
46008
|
} catch {
|
|
@@ -45288,9 +46025,9 @@ async function revokeDeviceBestEffort() {
|
|
|
45288
46025
|
}
|
|
45289
46026
|
var uninstallCommand = new Command("uninstall").description("Uninstall Gipity").option("--yes", "Skip confirmation prompts").action(async (opts) => {
|
|
45290
46027
|
const autoYes = opts.yes || getAutoConfirm();
|
|
45291
|
-
const gipityDir =
|
|
45292
|
-
const launcherBin =
|
|
45293
|
-
const installedViaLauncher =
|
|
46028
|
+
const gipityDir = join24(homedir17(), ".gipity");
|
|
46029
|
+
const launcherBin = join24(gipityDir, "launcher", "bin", "gipity");
|
|
46030
|
+
const installedViaLauncher = existsSync23(launcherBin);
|
|
45294
46031
|
console.log(`${bold("Gipity uninstall")} - this will:`);
|
|
45295
46032
|
console.log(`\u2022 Stop the running relay daemon (if any)`);
|
|
45296
46033
|
console.log(`\u2022 Remove the OS autostart service (launchd / systemd / Task Scheduler)`);
|
|
@@ -45325,9 +46062,26 @@ var uninstallCommand = new Command("uninstall").description("Uninstall Gipity").
|
|
|
45325
46062
|
} else {
|
|
45326
46063
|
console.log(`${muted("No Gipity entries in Claude Code settings.")}`);
|
|
45327
46064
|
}
|
|
45328
|
-
if (
|
|
46065
|
+
if (grokInstallState().exists) {
|
|
46066
|
+
spawnSyncCommand(resolveCommand("grok"), ["plugin", "uninstall", "gipity", "--confirm"], {
|
|
46067
|
+
stdio: "ignore",
|
|
46068
|
+
timeout: 6e4
|
|
46069
|
+
});
|
|
46070
|
+
console.log(`${success("Gipity plugin removed from Grok.")}`);
|
|
46071
|
+
}
|
|
46072
|
+
const agentSkills = agentSkillsState();
|
|
46073
|
+
if (agentSkills.skills.length) {
|
|
46074
|
+
for (const name of agentSkills.skills) {
|
|
46075
|
+
try {
|
|
46076
|
+
rmSync3(join24(AGENTS_SKILLS_DIR, name), { recursive: true, force: true });
|
|
46077
|
+
} catch {
|
|
46078
|
+
}
|
|
46079
|
+
}
|
|
46080
|
+
console.log(`${success(`Removed ${agentSkills.skills.length} Gipity skills from ~/.agents/skills.`)}`);
|
|
46081
|
+
}
|
|
46082
|
+
if (existsSync23(gipityDir)) {
|
|
45329
46083
|
try {
|
|
45330
|
-
|
|
46084
|
+
rmSync3(gipityDir, { recursive: true, force: true });
|
|
45331
46085
|
console.log(`${success(`Removed ${gipityDir}/`)}`);
|
|
45332
46086
|
} catch (err) {
|
|
45333
46087
|
console.error(`${error(`Could not remove ${gipityDir}: ${err?.message || err}`)}`);
|
|
@@ -45488,7 +46242,7 @@ gmailCommand.command("reply").description("Reply to a Gmail thread. Get thread-i
|
|
|
45488
46242
|
}));
|
|
45489
46243
|
|
|
45490
46244
|
// src/commands/text.ts
|
|
45491
|
-
import { readFileSync as
|
|
46245
|
+
import { readFileSync as readFileSync25 } from "fs";
|
|
45492
46246
|
init_colors();
|
|
45493
46247
|
|
|
45494
46248
|
// src/helpers/text-analysis.ts
|
|
@@ -45643,10 +46397,10 @@ function areAnagrams(a, b7) {
|
|
|
45643
46397
|
|
|
45644
46398
|
// src/commands/text.ts
|
|
45645
46399
|
function resolveInput(args, opts) {
|
|
45646
|
-
if (opts.file) return
|
|
46400
|
+
if (opts.file) return readFileSync25(opts.file, "utf-8");
|
|
45647
46401
|
if (args.length) return args.join(" ");
|
|
45648
46402
|
try {
|
|
45649
|
-
return
|
|
46403
|
+
return readFileSync25(0, "utf-8");
|
|
45650
46404
|
} catch {
|
|
45651
46405
|
return "";
|
|
45652
46406
|
}
|
|
@@ -45803,6 +46557,7 @@ var FLAG_ALIASES = {
|
|
|
45803
46557
|
"--src": "--source-dir",
|
|
45804
46558
|
"--srcdir": "--source-dir",
|
|
45805
46559
|
"--parallel": "--concurrency",
|
|
46560
|
+
"--no-sync-output": "--discard-output",
|
|
45806
46561
|
"--max": "--limit",
|
|
45807
46562
|
"--from": "--since",
|
|
45808
46563
|
"--after": "--since",
|
|
@@ -45851,9 +46606,9 @@ function collectRealFlags(argv2, program3) {
|
|
|
45851
46606
|
}
|
|
45852
46607
|
|
|
45853
46608
|
// src/trace.ts
|
|
45854
|
-
import { openSync as openSync5, writeSync, mkdirSync as
|
|
45855
|
-
import { join as
|
|
45856
|
-
import { homedir as
|
|
46609
|
+
import { openSync as openSync5, writeSync, mkdirSync as mkdirSync17 } from "fs";
|
|
46610
|
+
import { join as join25 } from "path";
|
|
46611
|
+
import { homedir as homedir18 } from "os";
|
|
45857
46612
|
var TRACE_KEY = /* @__PURE__ */ Symbol.for("gipity.traceOutput");
|
|
45858
46613
|
function installOutputTrace(label2) {
|
|
45859
46614
|
if (process.env.GIPITY_TRACE_OUTPUT !== "1") return;
|
|
@@ -45864,10 +46619,10 @@ function installOutputTrace(label2) {
|
|
|
45864
46619
|
existing.emit({ event: "reenter", label: label2 });
|
|
45865
46620
|
return;
|
|
45866
46621
|
}
|
|
45867
|
-
const dir =
|
|
45868
|
-
|
|
46622
|
+
const dir = join25(process.env.GIPITY_DIR || join25(homedir18(), ".gipity"), "trace");
|
|
46623
|
+
mkdirSync17(dir, { recursive: true });
|
|
45869
46624
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", "_").replace(/:/g, "-");
|
|
45870
|
-
const fd2 = openSync5(
|
|
46625
|
+
const fd2 = openSync5(join25(dir, `${stamp}-pid${process.pid}.jsonl`), "a");
|
|
45871
46626
|
const emit = (rec) => {
|
|
45872
46627
|
try {
|
|
45873
46628
|
writeSync(fd2, JSON.stringify({ t: (/* @__PURE__ */ new Date()).toISOString(), ...rec }) + "\n");
|
|
@@ -45903,11 +46658,11 @@ function installOutputTrace(label2) {
|
|
|
45903
46658
|
// src/index.ts
|
|
45904
46659
|
installOutputTrace("index");
|
|
45905
46660
|
var __dirname = dirname13(fileURLToPath3(import.meta.url));
|
|
45906
|
-
var pkg = JSON.parse(
|
|
46661
|
+
var pkg = JSON.parse(readFileSync26(resolve19(__dirname, "../package.json"), "utf-8"));
|
|
45907
46662
|
function versionLabel() {
|
|
45908
46663
|
const base = `v${pkg.version}`;
|
|
45909
46664
|
try {
|
|
45910
|
-
const info2 = JSON.parse(
|
|
46665
|
+
const info2 = JSON.parse(readFileSync26(resolve19(__dirname, "build-info.json"), "utf-8"));
|
|
45911
46666
|
if (info2?.sha) return `${base} (dev ${info2.sha}${info2.dirty ? ", modified" : ""})`;
|
|
45912
46667
|
} catch {
|
|
45913
46668
|
}
|
|
@@ -45949,7 +46704,7 @@ var servicesGroup = [serviceCommand, generateCommand, notifyCommand, paymentsCom
|
|
|
45949
46704
|
var filesGroup = [syncCommand, fileCommand, pushCommand, uploadCommand, storageCommand];
|
|
45950
46705
|
var gipGroup = [chatCommand, memoryCommand, agentCommand, approvalCommand, gmailCommand];
|
|
45951
46706
|
var utilitiesGroup = [sandboxCommand, emailCommand, locationCommand, textCommand, bugCommand];
|
|
45952
|
-
var connectGroup = [loginCommand, logoutCommand,
|
|
46707
|
+
var connectGroup = [loginCommand, logoutCommand, buildCommand, connectCommand, relayCommand, githubCommand, creditsCommand, doctorCommand, updateCommand, uninstallCommand];
|
|
45953
46708
|
var HELP_SECTIONS = [
|
|
45954
46709
|
{ title: "Start here", cmds: startGroup },
|
|
45955
46710
|
{ title: "App build & ship", cmds: buildGroup },
|
|
@@ -46007,9 +46762,9 @@ program2.configureHelp({
|
|
|
46007
46762
|
lines.push("");
|
|
46008
46763
|
lines.push(bold("Quick start:"));
|
|
46009
46764
|
lines.push(` ${brand("gipity login")} ${dim("- authenticate first if you haven't already")}`);
|
|
46010
|
-
lines.push(` ${brand("gipity init")} ${dim("- link this dir +
|
|
46011
|
-
lines.push(` ${brand("gipity
|
|
46012
|
-
lines.push(` ${brand("gipity
|
|
46765
|
+
lines.push(` ${brand("gipity init")} ${dim("- link this dir + wire up every coding agent on this machine")}`);
|
|
46766
|
+
lines.push(` ${brand("gipity connect")} ${dim("- connect this computer to gipity.ai so the web CLI can drive it")}`);
|
|
46767
|
+
lines.push(` ${brand("gipity build")} ${dim("- or start from anywhere: pick a project, pick your agent, go")}`);
|
|
46013
46768
|
lines.push("");
|
|
46014
46769
|
lines.push(bold("Usage:"));
|
|
46015
46770
|
lines.push(` ${cmd.name()} [options] [command]`);
|
|
@@ -46043,6 +46798,8 @@ for (const cmd of HELP_SECTIONS.flatMap((s) => s.cmds)) {
|
|
|
46043
46798
|
`);
|
|
46044
46799
|
program2.addCommand(cmd);
|
|
46045
46800
|
}
|
|
46801
|
+
configureHelp(claudeCommand);
|
|
46802
|
+
program2.addCommand(claudeCommand, { hidden: true });
|
|
46046
46803
|
program2.helpCommand(false);
|
|
46047
46804
|
function manifestCommand(c) {
|
|
46048
46805
|
return {
|