gipity 1.0.429 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/claude-code.js +53 -0
- package/dist/agents/codex.js +49 -0
- package/dist/agents/grok.js +54 -0
- package/dist/agents/index.js +23 -0
- package/dist/agents/types.js +18 -0
- package/dist/api.js +7 -0
- package/dist/capture/sources/codex.js +216 -0
- package/dist/capture/sources/grok.js +178 -0
- package/dist/client-context.js +2 -0
- package/dist/commands/build.js +1352 -0
- package/dist/commands/chat.js +9 -3
- package/dist/commands/claude.js +4 -13
- package/dist/commands/db.js +12 -5
- package/dist/commands/doctor.js +8 -2
- package/dist/commands/init.js +9 -15
- package/dist/commands/page-eval.js +18 -7
- 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 +2 -2
- package/dist/commands/sandbox.js +62 -16
- package/dist/commands/setup.js +16 -10
- package/dist/commands/uninstall.js +25 -3
- package/dist/hooks/capture-runner.js +127 -38
- package/dist/index.js +1055 -330
- package/dist/knowledge.js +3 -3
- package/dist/prefs.js +50 -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 +2 -2
- 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
|
}
|
|
@@ -6705,7 +6706,7 @@ async function postForTarEntries(path5, body, retried = false) {
|
|
|
6705
6706
|
if (!res.body) throw new ApiError(500, "EMPTY_RESPONSE", "Server returned no body");
|
|
6706
6707
|
const extract3 = tar.extract();
|
|
6707
6708
|
const entries = [];
|
|
6708
|
-
const done = new Promise((
|
|
6709
|
+
const done = new Promise((resolve20, reject) => {
|
|
6709
6710
|
extract3.on("entry", (header, stream, next) => {
|
|
6710
6711
|
const chunks = [];
|
|
6711
6712
|
stream.on("data", (c) => chunks.push(c));
|
|
@@ -6716,7 +6717,7 @@ async function postForTarEntries(path5, body, retried = false) {
|
|
|
6716
6717
|
stream.on("error", reject);
|
|
6717
6718
|
stream.resume();
|
|
6718
6719
|
});
|
|
6719
|
-
extract3.on("finish", () =>
|
|
6720
|
+
extract3.on("finish", () => resolve20());
|
|
6720
6721
|
extract3.on("error", reject);
|
|
6721
6722
|
});
|
|
6722
6723
|
Readable.fromWeb(res.body).pipe(extract3);
|
|
@@ -6741,6 +6742,9 @@ async function sendMessage(message) {
|
|
|
6741
6742
|
if (res.data.conversationGuid !== config.conversationGuid) {
|
|
6742
6743
|
saveConfig({ ...config, conversationGuid: res.data.conversationGuid });
|
|
6743
6744
|
}
|
|
6745
|
+
if (res.data.costWarning && !res.data.content) {
|
|
6746
|
+
return res.data.costWarning.message;
|
|
6747
|
+
}
|
|
6744
6748
|
return res.data.content;
|
|
6745
6749
|
}
|
|
6746
6750
|
async function download(path5, retried = false) {
|
|
@@ -31858,9 +31862,9 @@ var {
|
|
|
31858
31862
|
// src/index.ts
|
|
31859
31863
|
init_config();
|
|
31860
31864
|
init_utils();
|
|
31861
|
-
import { readFileSync as
|
|
31865
|
+
import { readFileSync as readFileSync26 } from "fs";
|
|
31862
31866
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
31863
|
-
import { dirname as dirname13, resolve as
|
|
31867
|
+
import { dirname as dirname13, resolve as resolve19 } from "path";
|
|
31864
31868
|
|
|
31865
31869
|
// src/helpers/output.ts
|
|
31866
31870
|
var frameOpen = false;
|
|
@@ -31982,7 +31986,7 @@ Prefer the cheapest option that works - CLI and sandbox are instant and free, ap
|
|
|
31982
31986
|
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
31987
|
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
31988
|
|
|
31985
|
-
You are the developer. Write files in this directory -
|
|
31989
|
+
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
31990
|
|
|
31987
31991
|
## Use first-party services before reaching outside
|
|
31988
31992
|
|
|
@@ -32018,7 +32022,7 @@ The full "when to add a template" rule and the definition of done are spelled ou
|
|
|
32018
32022
|
|
|
32019
32023
|
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
32024
|
|
|
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"
|
|
32025
|
+
\`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
32026
|
|
|
32023
32027
|
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
32028
|
|
|
@@ -32074,7 +32078,7 @@ Every tool call returns its full output with that call. There is no output buffe
|
|
|
32074
32078
|
|
|
32075
32079
|
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
32080
|
|
|
32077
|
-
Write files locally -
|
|
32081
|
+
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
32082
|
|
|
32079
32083
|
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
32084
|
|
|
@@ -32290,7 +32294,7 @@ function run(label2, action) {
|
|
|
32290
32294
|
init_api();
|
|
32291
32295
|
init_config();
|
|
32292
32296
|
init_utils();
|
|
32293
|
-
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync5, statSync, lstatSync, unlinkSync as unlinkSync3, readdirSync as
|
|
32297
|
+
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
32298
|
import { join as join4, relative, dirname as dirname4, extname as extname2, resolve as resolve4, sep } from "path";
|
|
32295
32299
|
import { hostname } from "os";
|
|
32296
32300
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -32354,25 +32358,25 @@ async function hashFile(path5) {
|
|
|
32354
32358
|
const hash = createHash("sha256");
|
|
32355
32359
|
let size = 0;
|
|
32356
32360
|
const stream = createReadStream(path5);
|
|
32357
|
-
await new Promise((
|
|
32361
|
+
await new Promise((resolve20, reject) => {
|
|
32358
32362
|
stream.on("data", (chunk) => {
|
|
32359
32363
|
const buf = chunk;
|
|
32360
32364
|
hash.update(buf);
|
|
32361
32365
|
size += buf.length;
|
|
32362
32366
|
});
|
|
32363
|
-
stream.on("end", () =>
|
|
32367
|
+
stream.on("end", () => resolve20());
|
|
32364
32368
|
stream.on("error", reject);
|
|
32365
32369
|
});
|
|
32366
32370
|
return { sha256: hash.digest("hex"), size };
|
|
32367
32371
|
}
|
|
32368
32372
|
function readRange(path5, start, end) {
|
|
32369
|
-
return new Promise((
|
|
32373
|
+
return new Promise((resolve20, reject) => {
|
|
32370
32374
|
const chunks = [];
|
|
32371
32375
|
const stream = createReadStream(path5, { start, end });
|
|
32372
32376
|
stream.on("data", (c) => {
|
|
32373
32377
|
chunks.push(c);
|
|
32374
32378
|
});
|
|
32375
|
-
stream.on("end", () =>
|
|
32379
|
+
stream.on("end", () => resolve20(Buffer.concat(chunks)));
|
|
32376
32380
|
stream.on("error", reject);
|
|
32377
32381
|
});
|
|
32378
32382
|
}
|
|
@@ -32536,11 +32540,14 @@ async function uploadCompleteBatch(projectGuid, items) {
|
|
|
32536
32540
|
// src/setup.ts
|
|
32537
32541
|
init_platform();
|
|
32538
32542
|
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";
|
|
32543
|
+
import { homedir as homedir3, tmpdir } from "os";
|
|
32544
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readFileSync as readFileSync5, mkdtempSync, rmSync, cpSync, readdirSync as readdirSync2 } from "fs";
|
|
32545
|
+
init_config();
|
|
32541
32546
|
var PRIMER_FILES = {
|
|
32542
32547
|
claude: "CLAUDE.md",
|
|
32543
32548
|
codex: "AGENTS.md",
|
|
32549
|
+
grok: "AGENTS.md",
|
|
32550
|
+
// Grok Build reads the AGENTS.md family (and CLAUDE.md) natively
|
|
32544
32551
|
aider: "AGENTS.md",
|
|
32545
32552
|
// shares the Codex primer; aider is pointed at it via .aider.conf.yml
|
|
32546
32553
|
gemini: "GEMINI.md",
|
|
@@ -32555,6 +32562,7 @@ var DEFAULT_SYNC_IGNORE = [
|
|
|
32555
32562
|
".gipity.json",
|
|
32556
32563
|
".gipity/",
|
|
32557
32564
|
".claude/",
|
|
32565
|
+
".codex/",
|
|
32558
32566
|
".gitignore",
|
|
32559
32567
|
AIDER_CONF_FILE,
|
|
32560
32568
|
// Home-directory junk: a project created inside a real home dir (or one that
|
|
@@ -32615,7 +32623,7 @@ var GIPITY_PLUGIN_ID = "gipity@gipity";
|
|
|
32615
32623
|
var GIPITY_MARKETPLACE_NAME = "gipity";
|
|
32616
32624
|
var GIPITY_MARKETPLACE_REPO = "GipityAI/skills";
|
|
32617
32625
|
var LEGACY_MARKETPLACE_REPO = "GipityAI/claude-plugin";
|
|
32618
|
-
var GIPITY_PLUGIN_VERSION = "0.
|
|
32626
|
+
var GIPITY_PLUGIN_VERSION = "0.7.0";
|
|
32619
32627
|
function isGipityManagedHookCommand(command) {
|
|
32620
32628
|
return (
|
|
32621
32629
|
// Capture hooks: bare absolute runner path or the fire-time launcher.
|
|
@@ -32702,8 +32710,8 @@ function userScopeInstallState() {
|
|
|
32702
32710
|
return { exists: false, current: false };
|
|
32703
32711
|
}
|
|
32704
32712
|
}
|
|
32705
|
-
function
|
|
32706
|
-
const probe = spawnSyncCommand(process.platform === "win32" ? "where" : "which", [
|
|
32713
|
+
function binaryOnPath(bin) {
|
|
32714
|
+
const probe = spawnSyncCommand(process.platform === "win32" ? "where" : "which", [bin], {
|
|
32707
32715
|
encoding: "utf-8"
|
|
32708
32716
|
});
|
|
32709
32717
|
return probe.status === 0 && !!probe.stdout?.toString().trim();
|
|
@@ -32711,7 +32719,7 @@ function claudeOnPath() {
|
|
|
32711
32719
|
function ensureGipityPluginInstalled() {
|
|
32712
32720
|
const state = userScopeInstallState();
|
|
32713
32721
|
if (state.current) return;
|
|
32714
|
-
if (!
|
|
32722
|
+
if (!binaryOnPath("claude")) return;
|
|
32715
32723
|
const claudeCmd = resolveCommand("claude");
|
|
32716
32724
|
spawnSyncCommand(claudeCmd, ["plugin", "marketplace", "update", GIPITY_MARKETPLACE_NAME], {
|
|
32717
32725
|
stdio: "ignore",
|
|
@@ -32723,6 +32731,146 @@ function ensureGipityPluginInstalled() {
|
|
|
32723
32731
|
timeout: 12e4
|
|
32724
32732
|
});
|
|
32725
32733
|
}
|
|
32734
|
+
function grokInstallState() {
|
|
32735
|
+
try {
|
|
32736
|
+
const p = join3(homedir3(), ".grok", "installed-plugins", "registry.json");
|
|
32737
|
+
const data = JSON.parse(readFileSync5(p, "utf-8"));
|
|
32738
|
+
const versions = [];
|
|
32739
|
+
for (const repo of Object.values(data?.repos ?? {})) {
|
|
32740
|
+
const v7 = repo?.plugins?.gipity?.version;
|
|
32741
|
+
if (typeof v7 === "string") versions.push(v7);
|
|
32742
|
+
}
|
|
32743
|
+
return {
|
|
32744
|
+
exists: versions.length > 0,
|
|
32745
|
+
current: versions.some((v7) => versionGte(v7, GIPITY_PLUGIN_VERSION))
|
|
32746
|
+
};
|
|
32747
|
+
} catch {
|
|
32748
|
+
return { exists: false, current: false };
|
|
32749
|
+
}
|
|
32750
|
+
}
|
|
32751
|
+
function ensureGrokPluginInstalled() {
|
|
32752
|
+
const state = grokInstallState();
|
|
32753
|
+
if (state.current) return;
|
|
32754
|
+
if (!binaryOnPath("grok")) return;
|
|
32755
|
+
const grokCmd = resolveCommand("grok");
|
|
32756
|
+
const verb = state.exists ? ["plugin", "update", "gipity"] : ["plugin", "install", GIPITY_MARKETPLACE_REPO, "--trust"];
|
|
32757
|
+
const res = spawnSyncCommand(grokCmd, verb, { stdio: "ignore", timeout: 12e4 });
|
|
32758
|
+
if (!state.exists && res.status === 0) {
|
|
32759
|
+
console.log("Installed the Gipity plugin for Grok (skills + file-sync hooks).");
|
|
32760
|
+
}
|
|
32761
|
+
}
|
|
32762
|
+
var AGENTS_SKILLS_DIR = join3(homedir3(), ".agents", "skills");
|
|
32763
|
+
var AGENT_HOOKS_DIR = join3(homedir3(), ".gipity", "agent-hooks");
|
|
32764
|
+
var AGENT_SKILLS_MANIFEST = join3(homedir3(), ".gipity", "agent-skills.json");
|
|
32765
|
+
function agentSkillsState() {
|
|
32766
|
+
try {
|
|
32767
|
+
const m = JSON.parse(readFileSync5(AGENT_SKILLS_MANIFEST, "utf-8"));
|
|
32768
|
+
return {
|
|
32769
|
+
current: typeof m?.version === "string" && versionGte(m.version, GIPITY_PLUGIN_VERSION),
|
|
32770
|
+
skills: Array.isArray(m?.skills) ? m.skills : []
|
|
32771
|
+
};
|
|
32772
|
+
} catch {
|
|
32773
|
+
return { current: false, skills: [] };
|
|
32774
|
+
}
|
|
32775
|
+
}
|
|
32776
|
+
function ensureAgentSkillsInstalled() {
|
|
32777
|
+
if (agentSkillsState().current) return;
|
|
32778
|
+
if (!binaryOnPath("git")) return;
|
|
32779
|
+
const tmp = mkdtempSync(join3(tmpdir(), "gipity-skills-"));
|
|
32780
|
+
try {
|
|
32781
|
+
const clone = spawnSyncCommand(
|
|
32782
|
+
resolveCommand("git"),
|
|
32783
|
+
["clone", "--depth", "1", `https://github.com/${GIPITY_MARKETPLACE_REPO}.git`, join3(tmp, "repo")],
|
|
32784
|
+
{ stdio: "ignore", timeout: 12e4 }
|
|
32785
|
+
);
|
|
32786
|
+
if (clone.status !== 0) return;
|
|
32787
|
+
const repo = join3(tmp, "repo");
|
|
32788
|
+
let version = GIPITY_PLUGIN_VERSION;
|
|
32789
|
+
try {
|
|
32790
|
+
const manifest = JSON.parse(readFileSync5(join3(repo, ".claude-plugin", "plugin.json"), "utf-8"));
|
|
32791
|
+
if (typeof manifest?.version === "string") version = manifest.version;
|
|
32792
|
+
} catch {
|
|
32793
|
+
}
|
|
32794
|
+
const skillsSrc = join3(repo, "skills");
|
|
32795
|
+
const names = [];
|
|
32796
|
+
for (const entry of readdirSync2(skillsSrc, { withFileTypes: true })) {
|
|
32797
|
+
if (!entry.isDirectory()) continue;
|
|
32798
|
+
if (!existsSync4(join3(skillsSrc, entry.name, "SKILL.md"))) continue;
|
|
32799
|
+
mkdirSync3(AGENTS_SKILLS_DIR, { recursive: true });
|
|
32800
|
+
cpSync(join3(skillsSrc, entry.name), join3(AGENTS_SKILLS_DIR, entry.name), {
|
|
32801
|
+
recursive: true,
|
|
32802
|
+
force: true
|
|
32803
|
+
});
|
|
32804
|
+
names.push(entry.name);
|
|
32805
|
+
}
|
|
32806
|
+
mkdirSync3(AGENT_HOOKS_DIR, { recursive: true });
|
|
32807
|
+
for (const script of readdirSync2(join3(repo, "hooks", "scripts"))) {
|
|
32808
|
+
cpSync(join3(repo, "hooks", "scripts", script), join3(AGENT_HOOKS_DIR, script), { force: true });
|
|
32809
|
+
}
|
|
32810
|
+
writeFileSync4(AGENT_SKILLS_MANIFEST, JSON.stringify({ version, skills: names }, null, 2) + "\n");
|
|
32811
|
+
console.log(`Installed ${names.length} Gipity skills for Codex (~/.agents/skills).`);
|
|
32812
|
+
} catch {
|
|
32813
|
+
} finally {
|
|
32814
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
32815
|
+
}
|
|
32816
|
+
}
|
|
32817
|
+
function applyCodexHooks(existing) {
|
|
32818
|
+
const launcher = join3(AGENT_HOOKS_DIR, "launch.sh");
|
|
32819
|
+
const cmd = (script, ...args) => [`sh "${launcher}" "${join3(AGENT_HOOKS_DIR, script)}"`, ...args].join(" ");
|
|
32820
|
+
const wanted = [
|
|
32821
|
+
{ event: "PostToolUse", matcher: "Edit|Write", command: cmd("sync-push.cjs"), timeout: 30 },
|
|
32822
|
+
{ event: "UserPromptSubmit", command: cmd("sync-pull.cjs"), timeout: 300 },
|
|
32823
|
+
// Session capture: mirror the Codex session into the Gipity web CLI.
|
|
32824
|
+
{ event: "SessionStart", command: cmd("capture.cjs", "codex", "session-start"), timeout: 30 },
|
|
32825
|
+
{ event: "PostToolUse", command: cmd("capture.cjs", "codex", "post-tool-use"), timeout: 30 },
|
|
32826
|
+
{ event: "Stop", command: cmd("capture.cjs", "codex", "stop"), timeout: 60 }
|
|
32827
|
+
];
|
|
32828
|
+
let settings = {};
|
|
32829
|
+
if (existing !== null) {
|
|
32830
|
+
try {
|
|
32831
|
+
settings = JSON.parse(existing);
|
|
32832
|
+
} catch {
|
|
32833
|
+
return null;
|
|
32834
|
+
}
|
|
32835
|
+
}
|
|
32836
|
+
const hooks = settings.hooks ?? (settings.hooks = {});
|
|
32837
|
+
let changed = false;
|
|
32838
|
+
for (const w of wanted) {
|
|
32839
|
+
const groups = Array.isArray(hooks[w.event]) ? hooks[w.event] : hooks[w.event] = [];
|
|
32840
|
+
const present = groups.some(
|
|
32841
|
+
(g) => Array.isArray(g?.hooks) && g.hooks.some(
|
|
32842
|
+
(h) => typeof h?.command === "string" && h.command === w.command
|
|
32843
|
+
)
|
|
32844
|
+
);
|
|
32845
|
+
if (present) continue;
|
|
32846
|
+
const group = { hooks: [{ type: "command", command: w.command, timeout: w.timeout }] };
|
|
32847
|
+
if (w.matcher) group.matcher = w.matcher;
|
|
32848
|
+
groups.push(group);
|
|
32849
|
+
changed = true;
|
|
32850
|
+
}
|
|
32851
|
+
return changed ? JSON.stringify(settings, null, 2) + "\n" : null;
|
|
32852
|
+
}
|
|
32853
|
+
function setupCodexHooks() {
|
|
32854
|
+
if (process.platform === "win32") return;
|
|
32855
|
+
const cwd = resolve3(process.cwd());
|
|
32856
|
+
if (cwd === resolve3(homedir3())) return;
|
|
32857
|
+
const path5 = join3(cwd, ".codex", "hooks.json");
|
|
32858
|
+
const existing = existsSync4(path5) ? readFileSync5(path5, "utf-8") : null;
|
|
32859
|
+
const next = applyCodexHooks(existing);
|
|
32860
|
+
if (next === null) return;
|
|
32861
|
+
mkdirSync3(dirname3(path5), { recursive: true });
|
|
32862
|
+
writeFileSync4(path5, next);
|
|
32863
|
+
if (existing === null) {
|
|
32864
|
+
console.log("Wrote Codex sync + session-capture hooks (.codex/hooks.json) - approve them once with /hooks inside Codex.");
|
|
32865
|
+
} else {
|
|
32866
|
+
console.log("Updated Codex hooks (.codex/hooks.json) - if Codex asks, re-approve them via /hooks.");
|
|
32867
|
+
}
|
|
32868
|
+
}
|
|
32869
|
+
function setupCodexIntegration() {
|
|
32870
|
+
if (!binaryOnPath("codex")) return;
|
|
32871
|
+
ensureAgentSkillsInstalled();
|
|
32872
|
+
setupCodexHooks();
|
|
32873
|
+
}
|
|
32726
32874
|
function setupClaudeHooks() {
|
|
32727
32875
|
ensureGipityPlugin();
|
|
32728
32876
|
const cwd = resolve3(process.cwd());
|
|
@@ -32744,14 +32892,21 @@ function setupClaudeHooks() {
|
|
|
32744
32892
|
}
|
|
32745
32893
|
var GIPITY_BLOCK_BEGIN = "<!-- BEGIN GIPITY INTEGRATION - auto-generated by gipity, do not edit this block -->";
|
|
32746
32894
|
var GIPITY_BLOCK_END = "<!-- END GIPITY INTEGRATION -->";
|
|
32747
|
-
function renderManagedBlock() {
|
|
32748
|
-
|
|
32895
|
+
function renderManagedBlock(apiBase2) {
|
|
32896
|
+
let body = [SKILLS_CONTENT, BUILD_VS_NON_BUILD_RULE, DEFINITION_OF_DONE].join("\n\n");
|
|
32897
|
+
const base = apiBase2.replace(/\/+$/, "");
|
|
32898
|
+
if (base !== DEFAULT_API_BASE) {
|
|
32899
|
+
body = body.replaceAll(DEFAULT_API_BASE, base);
|
|
32900
|
+
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\`.`;
|
|
32901
|
+
const headingEnd = body.indexOf("\n");
|
|
32902
|
+
body = body.slice(0, headingEnd + 1) + "\n" + note + "\n" + body.slice(headingEnd + 1);
|
|
32903
|
+
}
|
|
32749
32904
|
return `${GIPITY_BLOCK_BEGIN}
|
|
32750
32905
|
${body}
|
|
32751
32906
|
${GIPITY_BLOCK_END}`;
|
|
32752
32907
|
}
|
|
32753
|
-
function applySkillsBlock(existing) {
|
|
32754
|
-
const block = renderManagedBlock();
|
|
32908
|
+
function applySkillsBlock(existing, apiBase2 = DEFAULT_API_BASE) {
|
|
32909
|
+
const block = renderManagedBlock(apiBase2);
|
|
32755
32910
|
if (existing === null) return block + "\n";
|
|
32756
32911
|
let next;
|
|
32757
32912
|
const beginIdx = existing.indexOf(GIPITY_BLOCK_BEGIN);
|
|
@@ -32770,7 +32925,7 @@ function writeSkillsFile(relPath, wrap) {
|
|
|
32770
32925
|
const path5 = resolve3(process.cwd(), relPath);
|
|
32771
32926
|
mkdirSync3(dirname3(path5), { recursive: true });
|
|
32772
32927
|
const existing = existsSync4(path5) ? readFileSync5(path5, "utf-8") : null;
|
|
32773
|
-
const baseNext = applySkillsBlock(existing);
|
|
32928
|
+
const baseNext = applySkillsBlock(existing, resolveApiBase());
|
|
32774
32929
|
const next = wrap && existing === null ? wrap(baseNext) : baseNext;
|
|
32775
32930
|
if (next !== existing) writeFileSync4(path5, next);
|
|
32776
32931
|
}
|
|
@@ -32834,14 +32989,22 @@ ${block}`
|
|
|
32834
32989
|
);
|
|
32835
32990
|
}
|
|
32836
32991
|
var SUPPORTED_TOOLS = [
|
|
32837
|
-
{ key: "claude", label: "Claude Code (CLAUDE.md)", setup: setupClaudeMd },
|
|
32838
|
-
{ key: "codex", label: "OpenAI Codex (AGENTS.md)", setup: setupAgentsMd },
|
|
32992
|
+
{ key: "claude", label: "Claude Code (CLAUDE.md + Gipity plugin)", setup: setupClaudeMd, integrate: setupClaudeHooks },
|
|
32993
|
+
{ key: "codex", label: "OpenAI Codex (AGENTS.md + skills + sync hooks)", setup: setupAgentsMd, integrate: setupCodexIntegration },
|
|
32994
|
+
{ key: "grok", label: "Grok Build (AGENTS.md + Gipity plugin)", setup: setupAgentsMd, integrate: ensureGrokPluginInstalled },
|
|
32839
32995
|
{ key: "aider", label: "Aider (AGENTS.md + .aider.conf.yml)", setup: setupAiderMd, optIn: true },
|
|
32840
32996
|
{ key: "gemini", label: "Gemini CLI (GEMINI.md)", setup: setupGeminiMd },
|
|
32841
32997
|
{ key: "copilot", label: "GitHub Copilot (.github/copilot-instructions.md)", setup: setupCopilotMd },
|
|
32842
32998
|
{ key: "cursor", label: "Cursor (.cursor/rules/gipity.mdc)", setup: setupCursorMd }
|
|
32843
32999
|
];
|
|
32844
33000
|
var DEFAULT_TOOLS = SUPPORTED_TOOLS.filter((t) => !t.optIn);
|
|
33001
|
+
function setupProjectTools(tools = DEFAULT_TOOLS) {
|
|
33002
|
+
for (const t of tools) {
|
|
33003
|
+
t.setup();
|
|
33004
|
+
t.integrate?.();
|
|
33005
|
+
}
|
|
33006
|
+
setupGitignore();
|
|
33007
|
+
}
|
|
32845
33008
|
function setupGitignore() {
|
|
32846
33009
|
const gitignorePath = resolve3(process.cwd(), ".gitignore");
|
|
32847
33010
|
const entries = [".gipity/", ".gipity.json", ...SCRATCH_IGNORE];
|
|
@@ -32986,7 +33149,7 @@ function walkLocal(root, ignorePatterns, baseline) {
|
|
|
32986
33149
|
function walk(dir) {
|
|
32987
33150
|
let entries;
|
|
32988
33151
|
try {
|
|
32989
|
-
entries =
|
|
33152
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
32990
33153
|
} catch {
|
|
32991
33154
|
return;
|
|
32992
33155
|
}
|
|
@@ -33065,7 +33228,7 @@ async function fetchRemote(projectGuid) {
|
|
|
33065
33228
|
function extractTarToMap(stream, idleMs, onBytes, keep) {
|
|
33066
33229
|
const extract3 = tar2.extract();
|
|
33067
33230
|
const files = /* @__PURE__ */ new Map();
|
|
33068
|
-
return new Promise((
|
|
33231
|
+
return new Promise((resolve20, reject) => {
|
|
33069
33232
|
let settled = false;
|
|
33070
33233
|
let idle;
|
|
33071
33234
|
const arm = () => {
|
|
@@ -33101,7 +33264,7 @@ function extractTarToMap(stream, idleMs, onBytes, keep) {
|
|
|
33101
33264
|
});
|
|
33102
33265
|
entryStream.resume();
|
|
33103
33266
|
});
|
|
33104
|
-
extract3.on("finish", () => done(
|
|
33267
|
+
extract3.on("finish", () => done(resolve20, files));
|
|
33105
33268
|
extract3.on("error", (e) => done(reject, e));
|
|
33106
33269
|
stream.on("error", (e) => done(reject, e));
|
|
33107
33270
|
arm();
|
|
@@ -33860,7 +34023,7 @@ async function syncInner(projectGuid, root, ignore2, opts, interactive) {
|
|
|
33860
34023
|
function cleanupEmptyDirs(root, emptiedDirs) {
|
|
33861
34024
|
const isEmpty = (dir) => {
|
|
33862
34025
|
try {
|
|
33863
|
-
return
|
|
34026
|
+
return readdirSync3(dir).length === 0;
|
|
33864
34027
|
} catch {
|
|
33865
34028
|
return false;
|
|
33866
34029
|
}
|
|
@@ -34144,7 +34307,7 @@ init_utils();
|
|
|
34144
34307
|
|
|
34145
34308
|
// src/adopt-cwd.ts
|
|
34146
34309
|
init_api();
|
|
34147
|
-
import { readdirSync as
|
|
34310
|
+
import { readdirSync as readdirSync5, statSync as statSync3 } from "fs";
|
|
34148
34311
|
import { join as join8, resolve as resolve6, sep as sep2, parse as parsePath } from "path";
|
|
34149
34312
|
import { homedir as homedir6 } from "os";
|
|
34150
34313
|
|
|
@@ -34152,7 +34315,7 @@ import { homedir as homedir6 } from "os";
|
|
|
34152
34315
|
init_config();
|
|
34153
34316
|
|
|
34154
34317
|
// src/template-vars.ts
|
|
34155
|
-
import { promises as fs, readdirSync as
|
|
34318
|
+
import { promises as fs, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
34156
34319
|
import { join as join5, relative as relative2, extname as extname3 } from "path";
|
|
34157
34320
|
var SUBSTITUTABLE_EXTS = /* @__PURE__ */ new Set([
|
|
34158
34321
|
".html",
|
|
@@ -34219,7 +34382,7 @@ function* walkTextFiles(root) {
|
|
|
34219
34382
|
const dir = stack.pop();
|
|
34220
34383
|
let entries;
|
|
34221
34384
|
try {
|
|
34222
|
-
entries =
|
|
34385
|
+
entries = readdirSync4(dir, { withFileTypes: true });
|
|
34223
34386
|
} catch {
|
|
34224
34387
|
continue;
|
|
34225
34388
|
}
|
|
@@ -34308,10 +34471,7 @@ async function finalizeLocalProject(opts) {
|
|
|
34308
34471
|
} catch (err) {
|
|
34309
34472
|
if (opts.sync === "strict") throw err;
|
|
34310
34473
|
}
|
|
34311
|
-
|
|
34312
|
-
if (tools.some((t) => t.key === "claude")) setupClaudeHooks();
|
|
34313
|
-
for (const t of tools) t.setup();
|
|
34314
|
-
setupGitignore();
|
|
34474
|
+
setupProjectTools(opts.tools ?? DEFAULT_TOOLS);
|
|
34315
34475
|
return { applied };
|
|
34316
34476
|
}
|
|
34317
34477
|
|
|
@@ -34490,7 +34650,7 @@ function scanForAdoption(cwd) {
|
|
|
34490
34650
|
if (truncated) return;
|
|
34491
34651
|
let entries;
|
|
34492
34652
|
try {
|
|
34493
|
-
entries =
|
|
34653
|
+
entries = readdirSync5(dir);
|
|
34494
34654
|
} catch {
|
|
34495
34655
|
return;
|
|
34496
34656
|
}
|
|
@@ -34526,7 +34686,7 @@ function scanForAdoption(cwd) {
|
|
|
34526
34686
|
function isLikelyEmpty(cwd) {
|
|
34527
34687
|
let entries;
|
|
34528
34688
|
try {
|
|
34529
|
-
entries =
|
|
34689
|
+
entries = readdirSync5(cwd);
|
|
34530
34690
|
} catch {
|
|
34531
34691
|
return true;
|
|
34532
34692
|
}
|
|
@@ -34564,7 +34724,7 @@ function canAdoptCwd(cwd) {
|
|
|
34564
34724
|
function countGitRepoChildren(dir) {
|
|
34565
34725
|
let entries;
|
|
34566
34726
|
try {
|
|
34567
|
-
entries =
|
|
34727
|
+
entries = readdirSync5(dir);
|
|
34568
34728
|
} catch {
|
|
34569
34729
|
return 0;
|
|
34570
34730
|
}
|
|
@@ -34676,14 +34836,15 @@ function resolveTools(forFlag) {
|
|
|
34676
34836
|
(t) => requested.includes(t.key) || !t.optIn && requested.includes("all")
|
|
34677
34837
|
);
|
|
34678
34838
|
}
|
|
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(
|
|
34839
|
+
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
34840
|
"--for <tools>",
|
|
34681
34841
|
`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
34842
|
).addHelpText("after", `
|
|
34683
34843
|
Examples:
|
|
34684
34844
|
$ gipity init Link cwd as a new project (slug = dir name).
|
|
34685
34845
|
$ gipity init my-app Link cwd with an explicit slug.
|
|
34686
|
-
$ gipity init --for codex
|
|
34846
|
+
$ gipity init --for codex AGENTS.md + Codex skills/sync hooks only.
|
|
34847
|
+
$ gipity init --for grok AGENTS.md + the Gipity plugin in Grok only.
|
|
34687
34848
|
$ gipity init --for cursor,gemini Write only the Cursor + Gemini primers.
|
|
34688
34849
|
$ gipity init --for aider AGENTS.md + a read: entry in .aider.conf.yml
|
|
34689
34850
|
(aider auto-reads nothing, so it's opt-in).
|
|
@@ -34704,9 +34865,6 @@ Working with an existing Gipity project:
|
|
|
34704
34865
|
process.exit(1);
|
|
34705
34866
|
}
|
|
34706
34867
|
const wantsClaude = tools.some((t) => t.key === "claude");
|
|
34707
|
-
const writeAllPrimers = () => {
|
|
34708
|
-
for (const t of tools) t.setup();
|
|
34709
|
-
};
|
|
34710
34868
|
const primerSummary = tools.map((t) => t.label).join(", ");
|
|
34711
34869
|
try {
|
|
34712
34870
|
const auth = getAuth();
|
|
@@ -34718,9 +34876,7 @@ Working with an existing Gipity project:
|
|
|
34718
34876
|
if (existsSync8(resolve7(cwd, ".gipity.json"))) {
|
|
34719
34877
|
const existing = getConfig();
|
|
34720
34878
|
console.log(`Already linked to ${info(`"${existing?.projectSlug ?? ""}"`)} ${muted(`(${existing?.projectGuid ?? ""})`)}`);
|
|
34721
|
-
|
|
34722
|
-
writeAllPrimers();
|
|
34723
|
-
setupGitignore();
|
|
34879
|
+
setupProjectTools(tools);
|
|
34724
34880
|
if (existing) {
|
|
34725
34881
|
let changed = false;
|
|
34726
34882
|
const cur = existing.ignore ?? (existing.ignore = []);
|
|
@@ -34769,7 +34925,7 @@ Working with an existing Gipity project:
|
|
|
34769
34925
|
const sizeStr = scan.truncated ? `>${formatBytes(ADOPT_THRESHOLDS.REFUSE_BYTES)}` : formatBytes(scan.bytes);
|
|
34770
34926
|
const fileStr = scan.truncated ? `>${ADOPT_THRESHOLDS.REFUSE_FILES}` : `${scan.files}`;
|
|
34771
34927
|
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
|
|
34928
|
+
console.error(muted('Move into a subdirectory, or use `gipity build` and pick "Create new project".'));
|
|
34773
34929
|
process.exit(1);
|
|
34774
34930
|
}
|
|
34775
34931
|
if (scan.tier === "moderate") {
|
|
@@ -34808,7 +34964,7 @@ Working with an existing Gipity project:
|
|
|
34808
34964
|
}
|
|
34809
34965
|
console.log(success(`Wrote primer files: ${primerSummary}.`));
|
|
34810
34966
|
if (wantsClaude) {
|
|
34811
|
-
console.log(success("Ready! Run
|
|
34967
|
+
console.log(success("Ready! Run your coding agent here (claude, codex, grok), or `gipity build` to launch one with a picker."));
|
|
34812
34968
|
if (opts.capture === false) {
|
|
34813
34969
|
console.log(muted("Session recording is off for this project (captureHooks: false in .gipity.json)."));
|
|
34814
34970
|
} else {
|
|
@@ -34983,7 +35139,7 @@ var pushCommand = new Command("push").description("Push one or more files").argu
|
|
|
34983
35139
|
|
|
34984
35140
|
// src/commands/upload.ts
|
|
34985
35141
|
init_config();
|
|
34986
|
-
import { statSync as statSync4, readdirSync as
|
|
35142
|
+
import { statSync as statSync4, readdirSync as readdirSync6 } from "fs";
|
|
34987
35143
|
import { join as join10, basename as basename2, posix, resolve as resolve10, dirname as dirname6 } from "path";
|
|
34988
35144
|
init_utils();
|
|
34989
35145
|
init_colors();
|
|
@@ -34992,7 +35148,7 @@ function walkFiles(root) {
|
|
|
34992
35148
|
const stack = [root];
|
|
34993
35149
|
while (stack.length) {
|
|
34994
35150
|
const dir = stack.pop();
|
|
34995
|
-
for (const entry of
|
|
35151
|
+
for (const entry of readdirSync6(dir, { withFileTypes: true })) {
|
|
34996
35152
|
const full = join10(dir, entry.name);
|
|
34997
35153
|
if (entry.isDirectory()) stack.push(full);
|
|
34998
35154
|
else if (entry.isFile()) out.push(full);
|
|
@@ -35105,7 +35261,7 @@ init_config();
|
|
|
35105
35261
|
// src/page-fixtures.ts
|
|
35106
35262
|
init_api();
|
|
35107
35263
|
init_config();
|
|
35108
|
-
import { readFileSync as readFileSync11, statSync as statSync5, existsSync as existsSync10, readdirSync as
|
|
35264
|
+
import { readFileSync as readFileSync11, statSync as statSync5, existsSync as existsSync10, readdirSync as readdirSync7 } from "node:fs";
|
|
35109
35265
|
import { basename as basename3, resolve as resolve11, relative as relative3, join as join11 } from "node:path";
|
|
35110
35266
|
var FIND_SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", ".gipity", "dist", "build", ".next", "coverage"]);
|
|
35111
35267
|
function findByBasename(root, name, limit = 3) {
|
|
@@ -35117,7 +35273,7 @@ function findByBasename(root, name, limit = 3) {
|
|
|
35117
35273
|
visited++;
|
|
35118
35274
|
let entries;
|
|
35119
35275
|
try {
|
|
35120
|
-
entries =
|
|
35276
|
+
entries = readdirSync7(dir, { withFileTypes: true });
|
|
35121
35277
|
} catch {
|
|
35122
35278
|
continue;
|
|
35123
35279
|
}
|
|
@@ -35190,7 +35346,7 @@ async function uploadCameraFeed(projectGuid, localPath) {
|
|
|
35190
35346
|
}
|
|
35191
35347
|
|
|
35192
35348
|
// src/commands/page-eval.ts
|
|
35193
|
-
var EVAL_NO_VALUE_HINT = "The eval ran but returned no JSON-serializable value. A
|
|
35349
|
+
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
35350
|
function normalizeEvalResult(raw) {
|
|
35195
35351
|
const trimmed = (raw ?? "").trim();
|
|
35196
35352
|
if (trimmed === "" || trimmed === "null" || trimmed === "undefined") {
|
|
@@ -35350,8 +35506,8 @@ function evalExecTimeoutMessage(result, budgetMs) {
|
|
|
35350
35506
|
` + (budgetOverrunHint("in-page budget", budgetMs) ?? "") + `
|
|
35351
35507
|
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
35508
|
}
|
|
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
|
|
35509
|
+
var JS_DECOY_FLAGS = ["--js", "--javascript", "--script", "--code", "--expr", "--eval", "--exec", "--action"];
|
|
35510
|
+
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
35511
|
"--step <expr>",
|
|
35356
35512
|
`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
35513
|
(val, prev) => [...prev, val],
|
|
@@ -35370,7 +35526,7 @@ var pageEvalCommand = new Command("eval").description("Evaluate JS in a real bro
|
|
|
35370
35526
|
).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
35527
|
"--timeout <ms>",
|
|
35372
35528
|
`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 () => {
|
|
35529
|
+
).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
35530
|
const decoy = JS_DECOY_FLAGS.find((f) => opts[f.slice(2)] !== void 0);
|
|
35375
35531
|
if (decoy) {
|
|
35376
35532
|
pageEvalCommand.error(
|
|
@@ -35524,6 +35680,8 @@ ${hint}`);
|
|
|
35524
35680
|
if (d.auth?.requested) {
|
|
35525
35681
|
const who = getAuth()?.email;
|
|
35526
35682
|
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)")}`);
|
|
35683
|
+
} else {
|
|
35684
|
+
console.log(muted("Auth: anonymous visitor (signed out; pass --auth to run as your Gipity account)"));
|
|
35527
35685
|
}
|
|
35528
35686
|
if (camera) console.log(`${muted("Camera:")} ${camera.name} ${muted("(played as the webcam feed; getUserMedia resolves)")}`);
|
|
35529
35687
|
if (hosted.length) console.log(`${muted("Fixtures:")} ${hosted.map((h) => h.name).join(", ")}`);
|
|
@@ -35653,7 +35811,10 @@ function fmtMs(ms2) {
|
|
|
35653
35811
|
return ms2 >= 1e3 ? `${(ms2 / 1e3).toFixed(1)}s` : `${ms2}ms`;
|
|
35654
35812
|
}
|
|
35655
35813
|
function printAuthLine(auth) {
|
|
35656
|
-
if (!auth?.requested)
|
|
35814
|
+
if (!auth?.requested) {
|
|
35815
|
+
console.log(`${label("Auth")} ${muted("anonymous visitor (signed out; pass --auth to capture as your Gipity account)")}`);
|
|
35816
|
+
return;
|
|
35817
|
+
}
|
|
35657
35818
|
const who = getAuth()?.email;
|
|
35658
35819
|
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
35820
|
}
|
|
@@ -35751,7 +35912,7 @@ function splitCsv(values) {
|
|
|
35751
35912
|
function appendOption(value, previous = []) {
|
|
35752
35913
|
return [...previous, value];
|
|
35753
35914
|
}
|
|
35754
|
-
var
|
|
35915
|
+
var ACTION_ALIAS_FLAGS = ["--eval", "--js", "--javascript", "--script", "--code", "--exec"];
|
|
35755
35916
|
function buildWaitForGate(selector, timeoutMs) {
|
|
35756
35917
|
const sel = JSON.stringify(selector);
|
|
35757
35918
|
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 +35921,14 @@ var WAIT_FOR_MAX_MS2 = 15e3;
|
|
|
35760
35921
|
var WAIT_FOR_DEFAULT_MS = 15e3;
|
|
35761
35922
|
var MAX_POST_LOAD_DELAY_MS = 3e4;
|
|
35762
35923
|
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
|
-
);
|
|
35924
|
+
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 () => {
|
|
35925
|
+
const aliasFlag = ACTION_ALIAS_FLAGS.find((f) => opts[f.slice(2)] !== void 0);
|
|
35926
|
+
if (aliasFlag) {
|
|
35927
|
+
console.error(muted(
|
|
35928
|
+
`Note: treating ${aliasFlag} as --action \u2014 it runs your JS in the page before the capture. --action is the canonical flag.`
|
|
35929
|
+
));
|
|
35769
35930
|
}
|
|
35931
|
+
const actionScript = [opts.action, ...ACTION_ALIAS_FLAGS.map((f) => opts[f.slice(2)])].filter(Boolean).join("\n");
|
|
35770
35932
|
const delayRaw = opts.wait ?? opts.postLoadDelay;
|
|
35771
35933
|
const chosenDelay = delayRaw !== void 0;
|
|
35772
35934
|
let postLoadDelayMs = chosenDelay ? parseInt(String(delayRaw), 10) : 1e3;
|
|
@@ -35800,6 +35962,20 @@ var pageScreenshotCommand = new Command("screenshot").description("Screenshot a
|
|
|
35800
35962
|
}
|
|
35801
35963
|
const deviceNames = splitCsv(opts.device);
|
|
35802
35964
|
const viewportStrs = splitCsv(opts.viewport);
|
|
35965
|
+
if (opts.width !== void 0 || opts.height !== void 0) {
|
|
35966
|
+
if (opts.width === void 0 || opts.height === void 0) {
|
|
35967
|
+
pageScreenshotCommand.error(
|
|
35968
|
+
"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)"
|
|
35969
|
+
);
|
|
35970
|
+
}
|
|
35971
|
+
if (!/^\d+$/.test(String(opts.width)) || !/^\d+$/.test(String(opts.height))) {
|
|
35972
|
+
pageScreenshotCommand.error("error: --width and --height must be integers (px)");
|
|
35973
|
+
}
|
|
35974
|
+
viewportStrs.push(`${opts.width}x${opts.height}`);
|
|
35975
|
+
if (parseInt(String(opts.width), 10) <= 500) {
|
|
35976
|
+
console.error(muted("Tip: --device mobile emulates a real handset (touch events, mobile user-agent, DPR) \u2014 a raw viewport only resizes the window."));
|
|
35977
|
+
}
|
|
35978
|
+
}
|
|
35803
35979
|
const customViewports = [
|
|
35804
35980
|
...deviceNames.map(resolveDevice),
|
|
35805
35981
|
...viewportStrs.map(parseViewportString)
|
|
@@ -35823,7 +35999,7 @@ var pageScreenshotCommand = new Command("screenshot").description("Screenshot a
|
|
|
35823
35999
|
}
|
|
35824
36000
|
const preCapture = [
|
|
35825
36001
|
opts.waitFor ? buildWaitForGate(opts.waitFor, waitForTimeoutMs) : "",
|
|
35826
|
-
|
|
36002
|
+
actionScript
|
|
35827
36003
|
].filter(Boolean).join("\n");
|
|
35828
36004
|
const body = {
|
|
35829
36005
|
url,
|
|
@@ -35945,7 +36121,7 @@ ${brand("@ " + dims)}${deviceTag}`);
|
|
|
35945
36121
|
printVfsLine(s);
|
|
35946
36122
|
}
|
|
35947
36123
|
}));
|
|
35948
|
-
for (const f of
|
|
36124
|
+
for (const f of ACTION_ALIAS_FLAGS) pageScreenshotCommand.addOption(new Option(`${f} <js>`, "Alias for --action").hideHelp());
|
|
35949
36125
|
pageScreenshotCommand.addHelpText("after", `
|
|
35950
36126
|
Examples:
|
|
35951
36127
|
gipity page screenshot "https://dev.gipity.ai/me/app/"
|
|
@@ -35992,7 +36168,7 @@ function shortUrl(url, truncate = true, maxLen = 100) {
|
|
|
35992
36168
|
const tailLen = Math.floor(keep / 2);
|
|
35993
36169
|
return result.slice(0, headLen) + "\u2026" + result.slice(-tailLen);
|
|
35994
36170
|
}
|
|
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) => {
|
|
36171
|
+
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
36172
|
if (opts.screenshot !== void 0) {
|
|
35997
36173
|
console.error(error("page inspect does not capture screenshots. Use page screenshot:"));
|
|
35998
36174
|
console.error(` gipity page screenshot ${url}${typeof opts.screenshot === "string" ? ` -o ${opts.screenshot}` : ""}`);
|
|
@@ -36066,6 +36242,8 @@ async function inspectPage(url, opts = {}) {
|
|
|
36066
36242
|
if (b7.auth?.requested) {
|
|
36067
36243
|
const who = getAuth()?.email;
|
|
36068
36244
|
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)")}`);
|
|
36245
|
+
} else {
|
|
36246
|
+
console.log(muted("Auth: anonymous visitor (signed out; pass --auth to load as your Gipity account)"));
|
|
36069
36247
|
}
|
|
36070
36248
|
console.log(`${muted("Title:")} ${b7.title || "(none)"}`);
|
|
36071
36249
|
console.log(`${muted("Elements:")} ${b7.elementCount || 0}`);
|
|
@@ -36336,11 +36514,15 @@ dbCommand.command("list").description("List databases").option("--all", "List da
|
|
|
36336
36514
|
}
|
|
36337
36515
|
}));
|
|
36338
36516
|
dbCommand.command("create <name>").description("Create a database").option("--json", "Output as JSON").action((name, opts) => run("Create", async () => {
|
|
36339
|
-
const
|
|
36517
|
+
const config = requireConfig();
|
|
36518
|
+
await post(`/projects/${config.projectGuid}/db/manage`, {
|
|
36519
|
+
action: "create",
|
|
36520
|
+
name
|
|
36521
|
+
});
|
|
36340
36522
|
if (opts.json) {
|
|
36341
|
-
console.log(JSON.stringify({
|
|
36523
|
+
console.log(JSON.stringify({ success: true, name }));
|
|
36342
36524
|
} else {
|
|
36343
|
-
console.log(
|
|
36525
|
+
console.log(success(`Created database '${name}'.`));
|
|
36344
36526
|
}
|
|
36345
36527
|
}));
|
|
36346
36528
|
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 +36610,8 @@ memoryCommand.command("delete <topic>").description("Delete a topic").option("--
|
|
|
36428
36610
|
// src/commands/sandbox.ts
|
|
36429
36611
|
init_api();
|
|
36430
36612
|
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";
|
|
36613
|
+
import { readFileSync as readFileSync13, existsSync as existsSync11, statSync as statSync6, mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
|
|
36614
|
+
import { dirname as dirname8, extname as extname4, relative as relative4, resolve as resolve12, sep as sep3 } from "path";
|
|
36433
36615
|
init_colors();
|
|
36434
36616
|
var LANG_MAP = {
|
|
36435
36617
|
js: "javascript",
|
|
@@ -36547,13 +36729,13 @@ function resolveRelativeCwd() {
|
|
|
36547
36729
|
return rel2.split(/[\\/]/).filter(Boolean).join("/");
|
|
36548
36730
|
}
|
|
36549
36731
|
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(
|
|
36732
|
+
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
36733
|
"--input <path>",
|
|
36552
36734
|
"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
36735
|
(v7, prev) => [...prev ?? [], v7]
|
|
36554
36736
|
).option(
|
|
36555
|
-
"--
|
|
36556
|
-
|
|
36737
|
+
"--discard-output <glob>",
|
|
36738
|
+
"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
36739
|
(v7, prev) => [...prev, v7],
|
|
36558
36740
|
[]
|
|
36559
36741
|
).option("--json", "Output as JSON").addHelpText("after", `
|
|
@@ -36561,6 +36743,11 @@ By default the whole project is auto-mirrored into /work/ (up to 1 GB) -
|
|
|
36561
36743
|
so your code can reference project files by their relative path, and any
|
|
36562
36744
|
file you write lands back in the project. No manual copy needed.
|
|
36563
36745
|
|
|
36746
|
+
Exception: tmp/ is scratch. An output written under tmp/ comes back to
|
|
36747
|
+
your local tmp/ for inspection but is never saved to the project - use it
|
|
36748
|
+
for previews and other look-once artifacts (no cleanup needed). To drop
|
|
36749
|
+
an output entirely, --discard-output <glob>.
|
|
36750
|
+
|
|
36564
36751
|
Use --input only for projects over the auto-mirror cap, or when you want
|
|
36565
36752
|
to restrict what the sandbox sees.
|
|
36566
36753
|
|
|
@@ -36577,6 +36764,11 @@ Examples:
|
|
|
36577
36764
|
$ gipity sandbox run --language python \\
|
|
36578
36765
|
"import pandas as pd; print(pd.read_csv('data/sales.csv').describe())"
|
|
36579
36766
|
|
|
36767
|
+
# Preview a generated PDF without saving the preview: write it to tmp/
|
|
36768
|
+
$ gipity sandbox run bash \\
|
|
36769
|
+
"pdftoppm -png -r 90 docs/report.pdf tmp/preview"
|
|
36770
|
+
# -> tmp/preview-1.png lands on local disk only; Read it, done.
|
|
36771
|
+
|
|
36580
36772
|
# Run a script file directly (language inferred from .py)
|
|
36581
36773
|
$ gipity sandbox run --file build_report.py
|
|
36582
36774
|
$ gipity sandbox run python build_report.py # same thing, interpreter shorthand
|
|
@@ -36598,6 +36790,13 @@ GCC/Rust).
|
|
|
36598
36790
|
let inlineCode;
|
|
36599
36791
|
let filePath = opts.file;
|
|
36600
36792
|
let langFromInterp;
|
|
36793
|
+
if (opts.code !== void 0) {
|
|
36794
|
+
if (args.length) {
|
|
36795
|
+
console.error(error("Pass the code once: either positionally or via --code, not both"));
|
|
36796
|
+
process.exit(1);
|
|
36797
|
+
}
|
|
36798
|
+
args = [opts.code];
|
|
36799
|
+
}
|
|
36601
36800
|
if (args.length >= 2 && INTERPRETERS[args[0].toLowerCase()] !== void 0) {
|
|
36602
36801
|
langFromInterp = INTERPRETERS[args[0].toLowerCase()];
|
|
36603
36802
|
const rest = args.slice(1).join(" ");
|
|
@@ -36627,9 +36826,9 @@ GCC/Rust).
|
|
|
36627
36826
|
}
|
|
36628
36827
|
}
|
|
36629
36828
|
const language = resolveLanguage({ langFromInterp, langOpt: opts.language, filePath, inlineCode });
|
|
36630
|
-
const
|
|
36631
|
-
if (
|
|
36632
|
-
console.error(error('--
|
|
36829
|
+
const discardOutput = opts.discardOutput ?? [];
|
|
36830
|
+
if (discardOutput.some((g) => !g.trim())) {
|
|
36831
|
+
console.error(error('--discard-output requires a non-empty glob (e.g. --discard-output "*.ppm")'));
|
|
36633
36832
|
process.exit(1);
|
|
36634
36833
|
}
|
|
36635
36834
|
const { config } = await resolveProjectContext();
|
|
@@ -36655,16 +36854,30 @@ GCC/Rust).
|
|
|
36655
36854
|
cwd,
|
|
36656
36855
|
// The filter must run SERVER-side (in the output extractor): skipping
|
|
36657
36856
|
// only in the CLI would still write the files into project storage and
|
|
36658
|
-
// make every later sync propose deleting them.
|
|
36659
|
-
|
|
36857
|
+
// make every later sync propose deleting them. (`noSyncOutput` is the
|
|
36858
|
+
// wire name for --discard-output.)
|
|
36859
|
+
noSyncOutput: discardOutput.length ? discardOutput : void 0
|
|
36660
36860
|
});
|
|
36661
36861
|
const res = opts.json ? await doRun() : await withSpinner("Running in sandbox\u2026", doRun, { done: null });
|
|
36662
36862
|
const pulledLocal = !!(res.data.outputFiles?.length && getConfigPath());
|
|
36663
36863
|
if (pulledLocal) {
|
|
36664
36864
|
await sync({ interactive: false, progress: opts.json ? void 0 : createProgressReporter() });
|
|
36665
36865
|
}
|
|
36866
|
+
const scratchWritten = [];
|
|
36867
|
+
if (res.data.scratchFiles?.length) {
|
|
36868
|
+
const base = resolve12(getProjectRoot() ?? process.cwd());
|
|
36869
|
+
for (const f of res.data.scratchFiles) {
|
|
36870
|
+
const rel2 = f.path.replace(/\\/g, "/");
|
|
36871
|
+
const abs = resolve12(base, rel2);
|
|
36872
|
+
if (abs !== base && !abs.startsWith(base + sep3)) continue;
|
|
36873
|
+
mkdirSync8(dirname8(abs), { recursive: true });
|
|
36874
|
+
writeFileSync9(abs, Buffer.from(f.contentBase64, "base64"));
|
|
36875
|
+
scratchWritten.push(rel2);
|
|
36876
|
+
}
|
|
36877
|
+
}
|
|
36666
36878
|
if (opts.json) {
|
|
36667
|
-
|
|
36879
|
+
const { scratchFiles: _omit, ...rest } = res.data;
|
|
36880
|
+
console.log(JSON.stringify({ ...rest, scratchFiles: scratchWritten, filesSynced: pulledLocal }));
|
|
36668
36881
|
} else {
|
|
36669
36882
|
if (res.data.autoMirrorSkipped) {
|
|
36670
36883
|
console.error(dim(`Note: ${res.data.autoMirrorSkipped.reason}`));
|
|
@@ -36694,8 +36907,12 @@ GCC/Rust).
|
|
|
36694
36907
|
if (pulledLocal) console.log(dim("Not pulled locally - run 'gipity sync' to fetch them."));
|
|
36695
36908
|
}
|
|
36696
36909
|
}
|
|
36910
|
+
if (scratchWritten.length > 0) {
|
|
36911
|
+
console.log("\nScratch outputs (local only - never synced or deployed):");
|
|
36912
|
+
for (const f of scratchWritten) console.log(`${f}`);
|
|
36913
|
+
}
|
|
36697
36914
|
if (res.data.skippedOutputFiles && res.data.skippedOutputFiles.length > 0) {
|
|
36698
|
-
console.log(dim("\
|
|
36915
|
+
console.log(dim("\nDiscarded (--discard-output):"));
|
|
36699
36916
|
for (const f of res.data.skippedOutputFiles) console.log(dim(`${f}`));
|
|
36700
36917
|
}
|
|
36701
36918
|
if (res.data.exitCode !== 0) {
|
|
@@ -36737,9 +36954,11 @@ ${syncResult.summary}`;
|
|
|
36737
36954
|
...a.remoteSize != null ? { size: a.remoteSize } : {}
|
|
36738
36955
|
}));
|
|
36739
36956
|
}
|
|
36957
|
+
const displayContent = res.data.content || res.data.costWarning?.message || "";
|
|
36740
36958
|
if (opts.json) {
|
|
36741
36959
|
console.log(JSON.stringify({
|
|
36742
|
-
content:
|
|
36960
|
+
content: displayContent,
|
|
36961
|
+
costWarning: res.data.costWarning ?? null,
|
|
36743
36962
|
toolsUsed: res.data.toolsUsed?.map((t) => ({
|
|
36744
36963
|
tool: t.toolName,
|
|
36745
36964
|
success: t.success,
|
|
@@ -36753,7 +36972,7 @@ ${syncResult.summary}`;
|
|
|
36753
36972
|
syncedFiles: syncChanges
|
|
36754
36973
|
}));
|
|
36755
36974
|
} else {
|
|
36756
|
-
console.log(
|
|
36975
|
+
console.log(displayContent);
|
|
36757
36976
|
if (res.data.toolsUsed && res.data.toolsUsed.length > 0) {
|
|
36758
36977
|
const toolNames = [...new Set(res.data.toolsUsed.map((t) => t.toolName))];
|
|
36759
36978
|
console.log(`
|
|
@@ -36799,7 +37018,7 @@ chatCommand.command("delete <guid>").description("Delete a chat").option("--json
|
|
|
36799
37018
|
init_api();
|
|
36800
37019
|
init_config();
|
|
36801
37020
|
import { join as join13 } from "path";
|
|
36802
|
-
import { mkdirSync as
|
|
37021
|
+
import { mkdirSync as mkdirSync9 } from "fs";
|
|
36803
37022
|
init_colors();
|
|
36804
37023
|
init_utils();
|
|
36805
37024
|
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 +37076,7 @@ projectCommand.command("create <name>").description("Create a project").option("
|
|
|
36857
37076
|
const res = await post("/projects", body);
|
|
36858
37077
|
const project = res.data;
|
|
36859
37078
|
const dir = join13(getProjectsRoot(), project.slug);
|
|
36860
|
-
|
|
37079
|
+
mkdirSync9(dir, { recursive: true });
|
|
36861
37080
|
const accountSlug = await getAccountSlug();
|
|
36862
37081
|
let agentGuid = "";
|
|
36863
37082
|
try {
|
|
@@ -36891,7 +37110,7 @@ projectCommand.command("create <name>").description("Create a project").option("
|
|
|
36891
37110
|
if (process.env.GIPITY_NON_INTERACTIVE === "1") {
|
|
36892
37111
|
console.log(`${muted("Next:")} switch to "${project.name}" in the sidebar.`);
|
|
36893
37112
|
} else {
|
|
36894
|
-
console.log(`${muted("Next:")} exit Claude (Ctrl+D), then run: ${brand("gipity
|
|
37113
|
+
console.log(`${muted("Next:")} exit Claude (Ctrl+D), then run: ${brand("gipity build")}`);
|
|
36895
37114
|
console.log(`${muted("Pick")} "${project.name}" ${muted(`- it'll be at the top of the list.`)}`);
|
|
36896
37115
|
}
|
|
36897
37116
|
}));
|
|
@@ -37112,7 +37331,7 @@ function printStepRuns(steps, emptyNote) {
|
|
|
37112
37331
|
}
|
|
37113
37332
|
}
|
|
37114
37333
|
var TERMINAL_RUN_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
37115
|
-
var sleep2 = (ms2) => new Promise((
|
|
37334
|
+
var sleep2 = (ms2) => new Promise((resolve20) => setTimeout(resolve20, ms2));
|
|
37116
37335
|
async function waitForRun(wfGuid, prevGuid, timeoutSec) {
|
|
37117
37336
|
const deadline = Date.now() + timeoutSec * 1e3;
|
|
37118
37337
|
let runGuid;
|
|
@@ -37729,14 +37948,15 @@ storageCommand.command("retention").description("View or adjust your file versio
|
|
|
37729
37948
|
printRetention(data, opts.reset === true || hasSet);
|
|
37730
37949
|
}));
|
|
37731
37950
|
|
|
37732
|
-
// src/commands/
|
|
37951
|
+
// src/commands/build.ts
|
|
37733
37952
|
init_platform();
|
|
37734
37953
|
init_auth();
|
|
37735
37954
|
init_api();
|
|
37736
|
-
import { join as
|
|
37737
|
-
import { existsSync as
|
|
37738
|
-
import { homedir as
|
|
37955
|
+
import { join as join17, dirname as dirname10, resolve as resolve14, basename as basename4, sep as sep5 } from "path";
|
|
37956
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync12, readFileSync as readFileSync17, writeFileSync as writeFileSync12, renameSync as renameSync2, readdirSync as readdirSync8, statSync as statSync7 } from "fs";
|
|
37957
|
+
import { homedir as homedir12 } from "os";
|
|
37739
37958
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
37959
|
+
import { randomUUID } from "crypto";
|
|
37740
37960
|
|
|
37741
37961
|
// src/login-flow.ts
|
|
37742
37962
|
init_api();
|
|
@@ -37775,7 +37995,7 @@ async function interactiveLogin() {
|
|
|
37775
37995
|
return getAuth();
|
|
37776
37996
|
}
|
|
37777
37997
|
|
|
37778
|
-
// src/commands/
|
|
37998
|
+
// src/commands/build.ts
|
|
37779
37999
|
init_config();
|
|
37780
38000
|
|
|
37781
38001
|
// src/prompts.ts
|
|
@@ -38053,7 +38273,7 @@ init_platform();
|
|
|
38053
38273
|
init_api();
|
|
38054
38274
|
import { hostname as hostname3, userInfo, platform as osPlatform2 } from "os";
|
|
38055
38275
|
import { resolve as resolve13, dirname as dirname9 } from "path";
|
|
38056
|
-
import { mkdirSync as
|
|
38276
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
38057
38277
|
|
|
38058
38278
|
// src/relay/machine-id.ts
|
|
38059
38279
|
import { createHash as createHash3 } from "crypto";
|
|
@@ -38166,8 +38386,8 @@ function startDaemon() {
|
|
|
38166
38386
|
function installAutostart(opts = {}) {
|
|
38167
38387
|
const stdio = opts.stdio ?? "ignore";
|
|
38168
38388
|
const plan2 = planFor({ cliPath: resolveCliPath() });
|
|
38169
|
-
|
|
38170
|
-
|
|
38389
|
+
mkdirSync10(dirname9(plan2.path), { recursive: true });
|
|
38390
|
+
writeFileSync10(plan2.path, plan2.content);
|
|
38171
38391
|
let ok = true;
|
|
38172
38392
|
for (const argv2 of plan2.enableCmds) {
|
|
38173
38393
|
const r = spawnSyncCommand(argv2[0], argv2.slice(1), { stdio });
|
|
@@ -38264,7 +38484,7 @@ async function runRelaySetup(opts) {
|
|
|
38264
38484
|
console.error(` ${dim("Skipping for now - we'll offer again next time. Or turn it on with `gipity setup`.")}`);
|
|
38265
38485
|
return false;
|
|
38266
38486
|
}
|
|
38267
|
-
const startNow = await confirm(" Start the relay now (and on future `gipity
|
|
38487
|
+
const startNow = await confirm(" Start the relay now (and on future `gipity build` runs)?", { default: "yes" });
|
|
38268
38488
|
if (startNow) {
|
|
38269
38489
|
startDaemon();
|
|
38270
38490
|
}
|
|
@@ -38275,7 +38495,7 @@ async function runRelaySetup(opts) {
|
|
|
38275
38495
|
if (!res.ok) {
|
|
38276
38496
|
if (isWsl()) {
|
|
38277
38497
|
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
|
|
38498
|
+
console.log(` ${muted("it started just now and `gipity build` restarts it. For boot-time auto-start:")}`);
|
|
38279
38499
|
console.log(` ${muted("add [boot] systemd=true to /etc/wsl.conf, restart WSL, then run")} ${brand("gipity relay install")}${muted(".")}`);
|
|
38280
38500
|
} else {
|
|
38281
38501
|
console.log(` ${muted("Autostart install returned non-zero - you can run")} ${brand("gipity relay install")} ${muted("later.")}`);
|
|
@@ -38309,19 +38529,19 @@ async function maybeOfferRelayOn() {
|
|
|
38309
38529
|
await runRelaySetup({ mode: "offer-once" });
|
|
38310
38530
|
}
|
|
38311
38531
|
|
|
38312
|
-
// src/commands/
|
|
38532
|
+
// src/commands/build.ts
|
|
38313
38533
|
init_utils();
|
|
38314
38534
|
init_colors();
|
|
38315
38535
|
|
|
38316
38536
|
// src/banner.ts
|
|
38317
38537
|
init_colors();
|
|
38318
38538
|
import { homedir as homedir9 } from "os";
|
|
38319
|
-
import { sep as
|
|
38539
|
+
import { sep as sep4 } from "path";
|
|
38320
38540
|
function tildify(p) {
|
|
38321
38541
|
const home = homedir9();
|
|
38322
38542
|
if (!home) return p;
|
|
38323
38543
|
if (p === home) return "~";
|
|
38324
|
-
if (p.startsWith(home +
|
|
38544
|
+
if (p.startsWith(home + sep4)) return "~" + p.slice(home.length);
|
|
38325
38545
|
return p;
|
|
38326
38546
|
}
|
|
38327
38547
|
var AI_MODELS = [
|
|
@@ -38613,14 +38833,200 @@ function ensureClaudeInstalled(opts = {}) {
|
|
|
38613
38833
|
return { installed: isClaudeInstalled(), alreadyPresent: false };
|
|
38614
38834
|
}
|
|
38615
38835
|
|
|
38616
|
-
// src/
|
|
38836
|
+
// src/agents/claude-code.ts
|
|
38837
|
+
var claudeCodeAdapter = {
|
|
38838
|
+
key: "claude",
|
|
38839
|
+
source: "claude_code",
|
|
38840
|
+
displayName: "Claude Code",
|
|
38841
|
+
providerName: "Anthropic",
|
|
38842
|
+
binary: "claude",
|
|
38843
|
+
models: [
|
|
38844
|
+
{ id: "opus", label: "Opus" },
|
|
38845
|
+
{ id: "sonnet", label: "Sonnet" },
|
|
38846
|
+
{ id: "haiku", label: "Haiku" }
|
|
38847
|
+
],
|
|
38848
|
+
buildInteractiveArgs({ resume, model }) {
|
|
38849
|
+
const args = [];
|
|
38850
|
+
if (model) args.push("--model", model);
|
|
38851
|
+
if (resume) args.push("--resume", resume);
|
|
38852
|
+
return args;
|
|
38853
|
+
},
|
|
38854
|
+
buildHeadlessArgs({ message, resume, model, bypassApprovals, jsonStream }) {
|
|
38855
|
+
const args = ["-p", message];
|
|
38856
|
+
if (bypassApprovals) args.push("--permission-mode", "bypassPermissions");
|
|
38857
|
+
if (model) args.push("--model", model);
|
|
38858
|
+
if (resume) args.push("--resume", resume);
|
|
38859
|
+
if (jsonStream) args.push("--output-format", "stream-json", "--verbose", "--include-partial-messages");
|
|
38860
|
+
return args;
|
|
38861
|
+
},
|
|
38862
|
+
sessionIdFromStreamEvent(event) {
|
|
38863
|
+
if (event && typeof event.session_id === "string" && event.session_id) return event.session_id;
|
|
38864
|
+
return null;
|
|
38865
|
+
},
|
|
38866
|
+
hooksSupportedOnPlatform() {
|
|
38867
|
+
return true;
|
|
38868
|
+
},
|
|
38869
|
+
// The daemon parses Claude's stream-json into ingest entries itself
|
|
38870
|
+
// (hook capture stands down via GIPITY_CAPTURE=off on dispatches).
|
|
38871
|
+
daemonStreamCapture: true,
|
|
38872
|
+
ensureInstalled() {
|
|
38873
|
+
return ensureClaudeInstalled().installed;
|
|
38874
|
+
},
|
|
38875
|
+
installHint: `npm install -g ${CLAUDE_PACKAGE}`
|
|
38876
|
+
};
|
|
38877
|
+
|
|
38878
|
+
// src/agents/codex.ts
|
|
38879
|
+
var codexAdapter = {
|
|
38880
|
+
key: "codex",
|
|
38881
|
+
source: "codex",
|
|
38882
|
+
displayName: "Codex",
|
|
38883
|
+
providerName: "OpenAI",
|
|
38884
|
+
binary: "codex",
|
|
38885
|
+
models: [
|
|
38886
|
+
{ id: "gpt-5.1-codex", label: "GPT-5.1 Codex" },
|
|
38887
|
+
{ id: "gpt-5.1-codex-mini", label: "GPT-5.1 Codex Mini" }
|
|
38888
|
+
],
|
|
38889
|
+
buildInteractiveArgs({ resume, model }) {
|
|
38890
|
+
const args = [];
|
|
38891
|
+
if (model) args.push("--model", model);
|
|
38892
|
+
if (resume) args.push("resume", resume);
|
|
38893
|
+
return args;
|
|
38894
|
+
},
|
|
38895
|
+
buildHeadlessArgs({ message, resume, model, bypassApprovals, jsonStream }) {
|
|
38896
|
+
const args = resume ? ["exec", "resume", resume, message] : ["exec", message];
|
|
38897
|
+
if (model) args.push("--model", model);
|
|
38898
|
+
if (bypassApprovals) {
|
|
38899
|
+
args.push(
|
|
38900
|
+
"-s",
|
|
38901
|
+
"workspace-write",
|
|
38902
|
+
"-c",
|
|
38903
|
+
"sandbox_workspace_write.network_access=true",
|
|
38904
|
+
"--dangerously-bypass-hook-trust",
|
|
38905
|
+
"--skip-git-repo-check"
|
|
38906
|
+
);
|
|
38907
|
+
}
|
|
38908
|
+
if (jsonStream) args.push("--json");
|
|
38909
|
+
return args;
|
|
38910
|
+
},
|
|
38911
|
+
sessionIdFromStreamEvent(event) {
|
|
38912
|
+
if (event?.type === "thread.started" && typeof event.thread_id === "string") {
|
|
38913
|
+
return event.thread_id;
|
|
38914
|
+
}
|
|
38915
|
+
return null;
|
|
38916
|
+
},
|
|
38917
|
+
hooksSupportedOnPlatform(platform2) {
|
|
38918
|
+
return platform2 !== "win32";
|
|
38919
|
+
},
|
|
38920
|
+
// No stream-json ingest mapper for Codex: relay dispatches keep hook
|
|
38921
|
+
// capture ON (GIPITY_CONVERSATION_GUID binds it) and the transcript
|
|
38922
|
+
// parser mirrors the session; the daemon tracks byte-level progress only.
|
|
38923
|
+
daemonStreamCapture: false,
|
|
38924
|
+
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.",
|
|
38925
|
+
installHint: "npm install -g @openai/codex"
|
|
38926
|
+
};
|
|
38927
|
+
|
|
38928
|
+
// src/agents/grok.ts
|
|
38929
|
+
var grokAdapter = {
|
|
38930
|
+
key: "grok",
|
|
38931
|
+
source: "grok",
|
|
38932
|
+
displayName: "Grok",
|
|
38933
|
+
providerName: "xAI",
|
|
38934
|
+
binary: "grok",
|
|
38935
|
+
models: [
|
|
38936
|
+
{ id: "grok-4.5", label: "Grok 4.5" },
|
|
38937
|
+
{ id: "grok-code", label: "Grok Code" }
|
|
38938
|
+
],
|
|
38939
|
+
buildInteractiveArgs({ resume, model }) {
|
|
38940
|
+
const args = [];
|
|
38941
|
+
if (model) args.push("--model", model);
|
|
38942
|
+
if (resume) args.push("--resume", resume);
|
|
38943
|
+
return args;
|
|
38944
|
+
},
|
|
38945
|
+
buildHeadlessArgs({ message, resume, model, bypassApprovals, jsonStream }) {
|
|
38946
|
+
const args = ["-p", message];
|
|
38947
|
+
if (model) args.push("--model", model);
|
|
38948
|
+
if (resume) args.push("--resume", resume);
|
|
38949
|
+
if (bypassApprovals) args.push("--always-approve");
|
|
38950
|
+
if (jsonStream) args.push("--output-format", "streaming-json");
|
|
38951
|
+
return args;
|
|
38952
|
+
},
|
|
38953
|
+
sessionIdFromStreamEvent(event) {
|
|
38954
|
+
const fromParams = event?.params?.sessionId;
|
|
38955
|
+
if (typeof fromParams === "string" && fromParams) return fromParams;
|
|
38956
|
+
if (typeof event?.sessionId === "string" && event.sessionId) return event.sessionId;
|
|
38957
|
+
return null;
|
|
38958
|
+
},
|
|
38959
|
+
hooksSupportedOnPlatform() {
|
|
38960
|
+
return true;
|
|
38961
|
+
},
|
|
38962
|
+
daemonStreamCapture: false,
|
|
38963
|
+
// Verified live (2026-07-13): grok -p fires NO plugin hooks - not even
|
|
38964
|
+
// SessionStart/Stop - so headless capture is launcher-driven: pin the
|
|
38965
|
+
// session id, then replay ~/.grok/sessions/<cwd>/<sid>/chat_history.jsonl
|
|
38966
|
+
// through the capture runner after the run.
|
|
38967
|
+
headlessCapture: {
|
|
38968
|
+
sessionIdArgs: (sessionId) => ["--session-id", sessionId]
|
|
38969
|
+
},
|
|
38970
|
+
installHint: "curl -fsSL https://grok.x.ai/install.sh | sh (or see docs.x.ai/grok-build)"
|
|
38971
|
+
};
|
|
38972
|
+
|
|
38973
|
+
// src/agents/index.ts
|
|
38974
|
+
var AGENT_ADAPTERS = [
|
|
38975
|
+
claudeCodeAdapter,
|
|
38976
|
+
codexAdapter,
|
|
38977
|
+
grokAdapter
|
|
38978
|
+
];
|
|
38979
|
+
var AGENT_KEYS = AGENT_ADAPTERS.map((a) => a.key);
|
|
38980
|
+
function getAdapter(key) {
|
|
38981
|
+
const found = AGENT_ADAPTERS.find((a) => a.key === key);
|
|
38982
|
+
if (!found) throw new Error(`Unknown agent "${key}". Valid: ${AGENT_KEYS.join(", ")}`);
|
|
38983
|
+
return found;
|
|
38984
|
+
}
|
|
38985
|
+
function getAdapterBySource(source) {
|
|
38986
|
+
const found = AGENT_ADAPTERS.find((a) => a.source === source);
|
|
38987
|
+
if (!found) throw new Error(`No agent adapter for source "${source}"`);
|
|
38988
|
+
return found;
|
|
38989
|
+
}
|
|
38990
|
+
|
|
38991
|
+
// src/prefs.ts
|
|
38992
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync11 } from "fs";
|
|
38993
|
+
import { homedir as homedir11 } from "os";
|
|
38994
|
+
import { join as join16 } from "path";
|
|
38995
|
+
var PREFS_PATH = join16(homedir11(), ".gipity", "prefs.json");
|
|
38996
|
+
function readPrefs() {
|
|
38997
|
+
if (!existsSync13(PREFS_PATH)) return {};
|
|
38998
|
+
try {
|
|
38999
|
+
const parsed = JSON.parse(readFileSync16(PREFS_PATH, "utf-8"));
|
|
39000
|
+
return {
|
|
39001
|
+
lastAgent: typeof parsed.lastAgent === "string" ? parsed.lastAgent : void 0,
|
|
39002
|
+
lastModel: parsed.lastModel && typeof parsed.lastModel === "object" ? parsed.lastModel : void 0
|
|
39003
|
+
};
|
|
39004
|
+
} catch {
|
|
39005
|
+
return {};
|
|
39006
|
+
}
|
|
39007
|
+
}
|
|
39008
|
+
function writePrefs(update) {
|
|
39009
|
+
try {
|
|
39010
|
+
const current = readPrefs();
|
|
39011
|
+
const next = {
|
|
39012
|
+
...current,
|
|
39013
|
+
...update,
|
|
39014
|
+
lastModel: { ...current.lastModel, ...update.lastModel }
|
|
39015
|
+
};
|
|
39016
|
+
mkdirSync11(join16(homedir11(), ".gipity"), { recursive: true });
|
|
39017
|
+
writeFileSync11(PREFS_PATH, JSON.stringify(next, null, 2) + "\n");
|
|
39018
|
+
} catch {
|
|
39019
|
+
}
|
|
39020
|
+
}
|
|
39021
|
+
|
|
39022
|
+
// src/commands/build.ts
|
|
38617
39023
|
var __clDir = dirname10(fileURLToPath2(import.meta.url));
|
|
38618
39024
|
function readOwnPkg(fromDir) {
|
|
38619
39025
|
for (let d = fromDir; ; d = dirname10(d)) {
|
|
38620
|
-
const p =
|
|
38621
|
-
if (
|
|
39026
|
+
const p = join17(d, "package.json");
|
|
39027
|
+
if (existsSync14(p)) {
|
|
38622
39028
|
try {
|
|
38623
|
-
const pkg2 = JSON.parse(
|
|
39029
|
+
const pkg2 = JSON.parse(readFileSync17(p, "utf-8"));
|
|
38624
39030
|
if (pkg2.name === "gipity") return pkg2;
|
|
38625
39031
|
} catch {
|
|
38626
39032
|
}
|
|
@@ -38669,12 +39075,12 @@ function localFsFallback(dir) {
|
|
|
38669
39075
|
const topLevelEntries = [];
|
|
38670
39076
|
const walk = (d, depth) => {
|
|
38671
39077
|
try {
|
|
38672
|
-
for (const name of
|
|
39078
|
+
for (const name of readdirSync8(d).sort()) {
|
|
38673
39079
|
if (isSyncIgnored(name)) continue;
|
|
38674
39080
|
let isDir = false;
|
|
38675
39081
|
let size = 0;
|
|
38676
39082
|
try {
|
|
38677
|
-
const st2 = statSync7(
|
|
39083
|
+
const st2 = statSync7(join17(d, name));
|
|
38678
39084
|
isDir = st2.isDirectory();
|
|
38679
39085
|
size = st2.isFile() ? st2.size : 0;
|
|
38680
39086
|
} catch {
|
|
@@ -38683,7 +39089,7 @@ function localFsFallback(dir) {
|
|
|
38683
39089
|
if (isDir) {
|
|
38684
39090
|
folderCount++;
|
|
38685
39091
|
if (depth === 0) topLevelEntries.push(`${name}/`);
|
|
38686
|
-
walk(
|
|
39092
|
+
walk(join17(d, name), depth + 1);
|
|
38687
39093
|
} else {
|
|
38688
39094
|
fileCount++;
|
|
38689
39095
|
totalBytes += size;
|
|
@@ -38716,8 +39122,8 @@ function hasStreamJsonFlag(args) {
|
|
|
38716
39122
|
}
|
|
38717
39123
|
function markFolderTrusted(dir) {
|
|
38718
39124
|
try {
|
|
38719
|
-
const file =
|
|
38720
|
-
const cfg =
|
|
39125
|
+
const file = join17(homedir12(), ".claude.json");
|
|
39126
|
+
const cfg = existsSync14(file) ? JSON.parse(readFileSync17(file, "utf-8")) : {};
|
|
38721
39127
|
if (typeof cfg !== "object" || cfg === null) return;
|
|
38722
39128
|
if (typeof cfg.projects !== "object" || cfg.projects === null) cfg.projects = {};
|
|
38723
39129
|
const entry = typeof cfg.projects[dir] === "object" && cfg.projects[dir] !== null ? cfg.projects[dir] : {};
|
|
@@ -38725,7 +39131,7 @@ function markFolderTrusted(dir) {
|
|
|
38725
39131
|
entry.hasTrustDialogAccepted = true;
|
|
38726
39132
|
cfg.projects[dir] = entry;
|
|
38727
39133
|
const tmp = `${file}.gipity-tmp`;
|
|
38728
|
-
|
|
39134
|
+
writeFileSync12(tmp, JSON.stringify(cfg, null, 2));
|
|
38729
39135
|
renameSync2(tmp, file);
|
|
38730
39136
|
} catch {
|
|
38731
39137
|
}
|
|
@@ -38747,10 +39153,22 @@ function suggestProjectName(existingSlugs) {
|
|
|
38747
39153
|
}
|
|
38748
39154
|
return `project-${Date.now().toString(36).slice(-6)}`;
|
|
38749
39155
|
}
|
|
38750
|
-
|
|
39156
|
+
function createLaunchCommand(name, cfg = {}) {
|
|
39157
|
+
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 (e.g. opus, sonnet - or skip it to use your agent's own default)").option("--bypass-approvals", "Skip the agent's interactive tool approvals (headless runs; the relay daemon sets this)").allowUnknownOption(true).allowExcessArguments(true);
|
|
39158
|
+
if (!cfg.presetAgent) {
|
|
39159
|
+
cmd.option("--agent <agent>", `Which coding agent to launch: ${AGENT_KEYS.join(", ")} (default: your last-used)`);
|
|
39160
|
+
}
|
|
39161
|
+
cmd.action(async (opts) => {
|
|
39162
|
+
await runLaunch(name, cfg, opts);
|
|
39163
|
+
});
|
|
39164
|
+
return cmd;
|
|
39165
|
+
}
|
|
39166
|
+
var buildCommand = createLaunchCommand("build");
|
|
39167
|
+
var claudeCommand = createLaunchCommand("claude", { presetAgent: "claude" });
|
|
39168
|
+
async function runLaunch(cmdName, cmdCfg, opts) {
|
|
38751
39169
|
try {
|
|
38752
39170
|
const runStart = Date.now();
|
|
38753
|
-
const rawArgs = process.argv.slice(process.argv.indexOf(
|
|
39171
|
+
const rawArgs = process.argv.slice(process.argv.indexOf(cmdName) + 1);
|
|
38754
39172
|
if (rawArgs[0] === "install") {
|
|
38755
39173
|
const r = ensureClaudeInstalled({ force: rawArgs.includes("--force") });
|
|
38756
39174
|
if (r.alreadyPresent) console.log(` ${success("Claude Code already installed.")}`);
|
|
@@ -38791,7 +39209,7 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
38791
39209
|
process.exit(1);
|
|
38792
39210
|
}
|
|
38793
39211
|
if (nonInteractive && !getConfig() && !opts.newProject && !opts.project) {
|
|
38794
|
-
console.error(` ${error("No Gipity project in cwd. Run `gipity
|
|
39212
|
+
console.error(` ${error("No Gipity project in cwd. Run `gipity build` (interactive) first, or pass --new-project / --project <slug>.")}`);
|
|
38795
39213
|
process.exit(1);
|
|
38796
39214
|
}
|
|
38797
39215
|
if (!nonInteractive) {
|
|
@@ -38830,7 +39248,7 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
38830
39248
|
let existing = getConfig();
|
|
38831
39249
|
let forceAdoptCwd = false;
|
|
38832
39250
|
if (existing && !opts.newProject && !opts.project && !nonInteractive) {
|
|
38833
|
-
const cwdHasConfig =
|
|
39251
|
+
const cwdHasConfig = existsSync14(resolve14(process.cwd(), ".gipity.json"));
|
|
38834
39252
|
if (!cwdHasConfig && isLikelyEmpty(process.cwd())) {
|
|
38835
39253
|
const ancestorRoot = dirname10(getConfigPath());
|
|
38836
39254
|
console.log(` ${bold("You are inside an existing Gipity project.")}
|
|
@@ -38879,8 +39297,8 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
38879
39297
|
}
|
|
38880
39298
|
project = found;
|
|
38881
39299
|
}
|
|
38882
|
-
const projectDir2 = opts.here ? process.cwd() :
|
|
38883
|
-
|
|
39300
|
+
const projectDir2 = opts.here ? process.cwd() : join17(getProjectsRoot(), project.slug);
|
|
39301
|
+
mkdirSync12(projectDir2, { recursive: true });
|
|
38884
39302
|
process.chdir(projectDir2);
|
|
38885
39303
|
clearConfigCache();
|
|
38886
39304
|
let agentGuid = "";
|
|
@@ -38907,10 +39325,7 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
38907
39325
|
} catch (err) {
|
|
38908
39326
|
console.log(` ${warning("Could not sync files (will retry on next prompt):")} ${err.message}`);
|
|
38909
39327
|
}
|
|
38910
|
-
|
|
38911
|
-
setupClaudeMd();
|
|
38912
|
-
setupAgentsMd();
|
|
38913
|
-
setupGitignore();
|
|
39328
|
+
setupProjectTools();
|
|
38914
39329
|
if (nonInteractive) {
|
|
38915
39330
|
headlessNewProject = isNewProject;
|
|
38916
39331
|
} else {
|
|
@@ -38922,10 +39337,7 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
38922
39337
|
console.log(` Project: ${brand(existing.projectSlug)} ${muted(`(${existing.projectGuid})`)}`);
|
|
38923
39338
|
console.log(` ${success("Already set up.")}
|
|
38924
39339
|
`);
|
|
38925
|
-
|
|
38926
|
-
setupClaudeMd();
|
|
38927
|
-
setupAgentsMd();
|
|
38928
|
-
setupGitignore();
|
|
39340
|
+
setupProjectTools();
|
|
38929
39341
|
let doSync = true;
|
|
38930
39342
|
const projectRoot = dirname10(getConfigPath());
|
|
38931
39343
|
const scan = scanForAdoption(projectRoot);
|
|
@@ -39008,8 +39420,8 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
39008
39420
|
} else {
|
|
39009
39421
|
project = result.project;
|
|
39010
39422
|
isNewProject = result.kind === "create-new";
|
|
39011
|
-
const projectDir2 =
|
|
39012
|
-
|
|
39423
|
+
const projectDir2 = join17(getProjectsRoot(), project.slug);
|
|
39424
|
+
mkdirSync12(projectDir2, { recursive: true });
|
|
39013
39425
|
process.chdir(projectDir2);
|
|
39014
39426
|
clearConfigCache();
|
|
39015
39427
|
try {
|
|
@@ -39037,15 +39449,44 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
39037
39449
|
}
|
|
39038
39450
|
}
|
|
39039
39451
|
initialPrompt = "";
|
|
39040
|
-
|
|
39041
|
-
setupClaudeMd();
|
|
39042
|
-
setupAgentsMd();
|
|
39043
|
-
setupGitignore();
|
|
39452
|
+
setupProjectTools();
|
|
39044
39453
|
console.log(` ${success(`Project "${project.name}" ready.`)}
|
|
39045
39454
|
`);
|
|
39046
39455
|
}
|
|
39047
39456
|
if (opts.setupOnly) {
|
|
39048
|
-
console.log(` Done. cd ${process.cwd()} && gipity
|
|
39457
|
+
console.log(` Done. cd ${process.cwd()} && gipity ${cmdName}`);
|
|
39458
|
+
return;
|
|
39459
|
+
}
|
|
39460
|
+
const prefs = readPrefs();
|
|
39461
|
+
let agentKey = cmdCfg.presetAgent ?? (typeof opts.agent === "string" ? opts.agent.toLowerCase() : void 0);
|
|
39462
|
+
if (agentKey && !AGENT_KEYS.includes(agentKey)) {
|
|
39463
|
+
console.error(` ${error(`Unknown agent "${agentKey}".`)} ${muted(`Valid: ${AGENT_KEYS.join(", ")}`)}`);
|
|
39464
|
+
process.exit(1);
|
|
39465
|
+
}
|
|
39466
|
+
if (!agentKey) {
|
|
39467
|
+
if (nonInteractive) {
|
|
39468
|
+
agentKey = prefs.lastAgent && AGENT_KEYS.includes(prefs.lastAgent) ? prefs.lastAgent : "claude";
|
|
39469
|
+
} else {
|
|
39470
|
+
agentKey = await pickAgent(prefs.lastAgent);
|
|
39471
|
+
}
|
|
39472
|
+
}
|
|
39473
|
+
const adapter = getAdapter(agentKey);
|
|
39474
|
+
const modelFlagGiven = rawArgs.some((a) => a === "--model" || a.startsWith("--model="));
|
|
39475
|
+
let pickedModel = null;
|
|
39476
|
+
if (!nonInteractive && !modelFlagGiven && !cmdCfg.presetAgent) {
|
|
39477
|
+
pickedModel = await pickModel(adapter, prefs.lastModel?.[adapter.key] ?? null);
|
|
39478
|
+
writePrefs({ lastAgent: adapter.key, lastModel: { [adapter.key]: pickedModel } });
|
|
39479
|
+
}
|
|
39480
|
+
if (adapter.key !== "claude") {
|
|
39481
|
+
await launchNonClaudeAgent(adapter, {
|
|
39482
|
+
cmdName,
|
|
39483
|
+
opts,
|
|
39484
|
+
rawArgs,
|
|
39485
|
+
nonInteractive,
|
|
39486
|
+
headlessOut,
|
|
39487
|
+
runStart,
|
|
39488
|
+
pickedModel
|
|
39489
|
+
});
|
|
39049
39490
|
return;
|
|
39050
39491
|
}
|
|
39051
39492
|
if (!isClaudeInstalled()) {
|
|
@@ -39071,7 +39512,7 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
39071
39512
|
if (resumeSid) {
|
|
39072
39513
|
try {
|
|
39073
39514
|
const found = await get(
|
|
39074
|
-
`/conversations/
|
|
39515
|
+
`/conversations/remote/by-session/${encodeURIComponent(resumeSid)}?source=claude_code`
|
|
39075
39516
|
);
|
|
39076
39517
|
convGuidForHooks = found.data.conversation_guid;
|
|
39077
39518
|
} catch {
|
|
@@ -39079,8 +39520,8 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
39079
39520
|
}
|
|
39080
39521
|
if (!convGuidForHooks && cfg?.projectGuid) {
|
|
39081
39522
|
const created = await post(
|
|
39082
|
-
"/conversations/
|
|
39083
|
-
{ project_guid: cfg.projectGuid, device_guid: device.guid, origin: "local" }
|
|
39523
|
+
"/conversations/remote",
|
|
39524
|
+
{ project_guid: cfg.projectGuid, device_guid: device.guid, source: "claude_code", origin: "local" }
|
|
39084
39525
|
);
|
|
39085
39526
|
convGuidForHooks = created.data.conversation_guid;
|
|
39086
39527
|
}
|
|
@@ -39091,10 +39532,10 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
39091
39532
|
}
|
|
39092
39533
|
}
|
|
39093
39534
|
}
|
|
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"];
|
|
39535
|
+
const cmdIdx = process.argv.indexOf(cmdName);
|
|
39536
|
+
const rawClaudeArgs = process.argv.slice(cmdIdx + 1);
|
|
39537
|
+
const gipityBooleanFlags = /* @__PURE__ */ new Set(["--setup-only", "--new-project", "--here", "--quiet", "--bypass-approvals"]);
|
|
39538
|
+
const gipityValueFlags = ["--api-base", "--name", "--project", "--agent"];
|
|
39098
39539
|
const claudeArgs = [];
|
|
39099
39540
|
for (let i = 0; i < rawClaudeArgs.length; i++) {
|
|
39100
39541
|
const arg = rawClaudeArgs[i];
|
|
@@ -39110,6 +39551,9 @@ var claudeCommand = new Command("claude").description("Set up and run Claude Cod
|
|
|
39110
39551
|
console.log(` ${bold("Launching Claude Code, powered by Gipity.")}`);
|
|
39111
39552
|
console.log(` ${muted("Just tell Claude what you'd like to build or do - everything Claude can do,")}`);
|
|
39112
39553
|
console.log(` ${muted("plus hosting, databases, and live deploys on Gipity.")}`);
|
|
39554
|
+
if (convGuidForHooks) {
|
|
39555
|
+
console.log(` ${muted(`This session is saved to Gipity - watch it live at ${brand("prompt.gipity.ai")}. (--no-capture on init to disable.)`)}`);
|
|
39556
|
+
}
|
|
39113
39557
|
console.log("");
|
|
39114
39558
|
}
|
|
39115
39559
|
let allArgs;
|
|
@@ -39150,6 +39594,7 @@ Sending to Claude Code: ${userMsg}
|
|
|
39150
39594
|
}
|
|
39151
39595
|
} else {
|
|
39152
39596
|
allArgs = initialPrompt ? [initialPrompt, ...claudeArgs] : claudeArgs;
|
|
39597
|
+
if (pickedModel) allArgs.push("--model", pickedModel);
|
|
39153
39598
|
}
|
|
39154
39599
|
if (nonInteractive && (opts.newProject || opts.project)) {
|
|
39155
39600
|
const hasPermFlag = allArgs.some((a) => a === "--permission-mode" || a.startsWith("--permission-mode=") || a === "--dangerously-skip-permissions");
|
|
@@ -39186,8 +39631,11 @@ Sending to Claude Code: ${userMsg}
|
|
|
39186
39631
|
${doneLine}
|
|
39187
39632
|
|
|
39188
39633
|
`);
|
|
39189
|
-
else
|
|
39634
|
+
else {
|
|
39635
|
+
console.log(`
|
|
39190
39636
|
${doneLine}`);
|
|
39637
|
+
console.log(` ${muted(`Next time: gipity build from anywhere, or cd ${process.cwd()} && claude.`)}`);
|
|
39638
|
+
}
|
|
39191
39639
|
process.exit(code ?? 0);
|
|
39192
39640
|
});
|
|
39193
39641
|
} catch (err) {
|
|
@@ -39195,7 +39643,201 @@ ${doneLine}
|
|
|
39195
39643
|
${error(`Error: ${err.message}`)}`);
|
|
39196
39644
|
process.exit(1);
|
|
39197
39645
|
}
|
|
39198
|
-
}
|
|
39646
|
+
}
|
|
39647
|
+
async function pickAgent(lastUsed) {
|
|
39648
|
+
const defaultKey = lastUsed && AGENT_KEYS.includes(lastUsed) ? lastUsed : "claude";
|
|
39649
|
+
const defaultIdx = AGENT_ADAPTERS.findIndex((a) => a.key === defaultKey) + 1;
|
|
39650
|
+
console.log(` ${bold("Which coding agent?")}
|
|
39651
|
+
`);
|
|
39652
|
+
AGENT_ADAPTERS.forEach((a, i) => {
|
|
39653
|
+
const installed = binaryOnPath(a.binary) || a.key === "claude" && isClaudeInstalled();
|
|
39654
|
+
const notes = [];
|
|
39655
|
+
if (a.key === lastUsed) notes.push("last used");
|
|
39656
|
+
if (!installed) notes.push(a.ensureInstalled ? "not installed - we'll install it" : "not installed");
|
|
39657
|
+
if (a.key === "codex" && !a.hooksSupportedOnPlatform(process.platform)) {
|
|
39658
|
+
notes.push("session recording unavailable on Windows");
|
|
39659
|
+
}
|
|
39660
|
+
const note = notes.length ? ` ${muted(`(${notes.join(", ")})`)}` : "";
|
|
39661
|
+
console.log(` ${bold(`${i + 1}.`)} ${a.displayName} ${muted(`(${a.providerName})`)}${note}`);
|
|
39662
|
+
});
|
|
39663
|
+
console.log("");
|
|
39664
|
+
const choice = await pickOne("Choose", AGENT_ADAPTERS.length, defaultIdx);
|
|
39665
|
+
console.log("");
|
|
39666
|
+
return AGENT_ADAPTERS[choice - 1].key;
|
|
39667
|
+
}
|
|
39668
|
+
async function pickModel(adapter, lastUsed) {
|
|
39669
|
+
console.log(` ${bold("Which model?")}
|
|
39670
|
+
`);
|
|
39671
|
+
const lastIsModel = lastUsed && adapter.models.some((m) => m.id === lastUsed);
|
|
39672
|
+
const defaultIdx = lastIsModel ? adapter.models.findIndex((m) => m.id === lastUsed) + 2 : 1;
|
|
39673
|
+
console.log(` ${bold("1.")} Agent default ${muted(defaultIdx === 1 ? "(recommended)" : "")}`);
|
|
39674
|
+
adapter.models.forEach((m, i) => {
|
|
39675
|
+
const note = m.id === lastUsed ? ` ${muted("(last used)")}` : "";
|
|
39676
|
+
console.log(` ${bold(`${i + 2}.`)} ${m.label}${note}`);
|
|
39677
|
+
});
|
|
39678
|
+
console.log("");
|
|
39679
|
+
const choice = await pickOne("Choose", adapter.models.length + 1, defaultIdx);
|
|
39680
|
+
console.log("");
|
|
39681
|
+
return choice === 1 ? null : adapter.models[choice - 2].id;
|
|
39682
|
+
}
|
|
39683
|
+
async function launchNonClaudeAgent(adapter, ctx) {
|
|
39684
|
+
const { opts, rawArgs, nonInteractive, headlessOut, runStart, pickedModel } = ctx;
|
|
39685
|
+
if (!binaryOnPath(adapter.binary)) {
|
|
39686
|
+
let installed = false;
|
|
39687
|
+
if (adapter.ensureInstalled) {
|
|
39688
|
+
console.log(` ${adapter.displayName} not found - installing it now...`);
|
|
39689
|
+
installed = adapter.ensureInstalled();
|
|
39690
|
+
}
|
|
39691
|
+
if (!installed && !binaryOnPath(adapter.binary)) {
|
|
39692
|
+
console.error(` ${error(`${adapter.displayName} is not installed.`)} Install it with: ${adapter.installHint}`);
|
|
39693
|
+
console.error(` ${muted(`Then run: gipity ${ctx.cmdName} --agent ${adapter.key}`)}`);
|
|
39694
|
+
process.exit(1);
|
|
39695
|
+
}
|
|
39696
|
+
}
|
|
39697
|
+
const booleanFlags = /* @__PURE__ */ new Set(["--setup-only", "--new-project", "--here", "--quiet", "--bypass-approvals"]);
|
|
39698
|
+
const valueFlags = ["--api-base", "--name", "--project", "--agent"];
|
|
39699
|
+
let message = null;
|
|
39700
|
+
let resume;
|
|
39701
|
+
let model = pickedModel ?? void 0;
|
|
39702
|
+
const extras = [];
|
|
39703
|
+
for (let i = 0; i < rawArgs.length; i++) {
|
|
39704
|
+
const arg = rawArgs[i];
|
|
39705
|
+
if (booleanFlags.has(arg)) continue;
|
|
39706
|
+
if (valueFlags.includes(arg)) {
|
|
39707
|
+
i++;
|
|
39708
|
+
continue;
|
|
39709
|
+
}
|
|
39710
|
+
if (valueFlags.some((f) => arg.startsWith(`${f}=`))) continue;
|
|
39711
|
+
if (arg === "-p" || arg === "--print") {
|
|
39712
|
+
message = rawArgs[++i] ?? "";
|
|
39713
|
+
continue;
|
|
39714
|
+
}
|
|
39715
|
+
if (arg.startsWith("-p=") || arg.startsWith("--print=")) {
|
|
39716
|
+
message = arg.slice(arg.indexOf("=") + 1);
|
|
39717
|
+
continue;
|
|
39718
|
+
}
|
|
39719
|
+
if (arg === "--model") {
|
|
39720
|
+
model = rawArgs[++i];
|
|
39721
|
+
continue;
|
|
39722
|
+
}
|
|
39723
|
+
if (arg.startsWith("--model=")) {
|
|
39724
|
+
model = arg.slice("--model=".length);
|
|
39725
|
+
continue;
|
|
39726
|
+
}
|
|
39727
|
+
if (arg === "--resume") {
|
|
39728
|
+
resume = rawArgs[++i];
|
|
39729
|
+
continue;
|
|
39730
|
+
}
|
|
39731
|
+
if (arg.startsWith("--resume=")) {
|
|
39732
|
+
resume = arg.slice("--resume=".length);
|
|
39733
|
+
continue;
|
|
39734
|
+
}
|
|
39735
|
+
extras.push(arg);
|
|
39736
|
+
}
|
|
39737
|
+
let convGuid = process.env.GIPITY_CONVERSATION_GUID ?? null;
|
|
39738
|
+
if (!convGuid) {
|
|
39739
|
+
const device = getDevice();
|
|
39740
|
+
const config = getConfig();
|
|
39741
|
+
if (device && config?.projectGuid) {
|
|
39742
|
+
try {
|
|
39743
|
+
if (resume) {
|
|
39744
|
+
try {
|
|
39745
|
+
const found = await get(
|
|
39746
|
+
`/conversations/remote/by-session/${encodeURIComponent(resume)}?source=${encodeURIComponent(adapter.source)}`
|
|
39747
|
+
);
|
|
39748
|
+
convGuid = found.data.conversation_guid;
|
|
39749
|
+
} catch {
|
|
39750
|
+
}
|
|
39751
|
+
}
|
|
39752
|
+
if (!convGuid) {
|
|
39753
|
+
const created = await post(
|
|
39754
|
+
"/conversations/remote",
|
|
39755
|
+
{ project_guid: config.projectGuid, device_guid: device.guid, source: adapter.source, origin: "local" }
|
|
39756
|
+
);
|
|
39757
|
+
convGuid = created.data.conversation_guid;
|
|
39758
|
+
}
|
|
39759
|
+
} catch (err) {
|
|
39760
|
+
if (!nonInteractive) {
|
|
39761
|
+
console.error(` ${error(`Could not create Gipity conversation: ${err?.message || err}`)}`);
|
|
39762
|
+
}
|
|
39763
|
+
}
|
|
39764
|
+
}
|
|
39765
|
+
}
|
|
39766
|
+
let agentArgs;
|
|
39767
|
+
let syntheticSid = null;
|
|
39768
|
+
if (nonInteractive) {
|
|
39769
|
+
const bypass = Boolean(opts.bypassApprovals || opts.newProject || opts.project);
|
|
39770
|
+
agentArgs = [
|
|
39771
|
+
...adapter.buildHeadlessArgs({ message: message ?? "", resume, model, bypassApprovals: bypass, jsonStream: false }),
|
|
39772
|
+
...extras
|
|
39773
|
+
];
|
|
39774
|
+
if (adapter.headlessCapture) {
|
|
39775
|
+
syntheticSid = resume ?? randomUUID();
|
|
39776
|
+
if (!resume) agentArgs.push(...adapter.headlessCapture.sessionIdArgs(syntheticSid));
|
|
39777
|
+
}
|
|
39778
|
+
if (message) headlessOut(`
|
|
39779
|
+
Sending to ${adapter.displayName}: ${message}
|
|
39780
|
+
`);
|
|
39781
|
+
} else {
|
|
39782
|
+
agentArgs = [...adapter.buildInteractiveArgs({ resume, model }), ...extras];
|
|
39783
|
+
console.log(` ${bold(`Launching ${adapter.displayName}, powered by Gipity.`)}`);
|
|
39784
|
+
console.log(` ${muted(`Just tell ${adapter.displayName} what you'd like to build or do - plus hosting,`)}`);
|
|
39785
|
+
console.log(` ${muted("databases, and live deploys on Gipity.")}`);
|
|
39786
|
+
if (convGuid) {
|
|
39787
|
+
console.log(` ${muted(`This session is saved to Gipity - watch it live at ${brand("prompt.gipity.ai")}. (--no-capture on init to disable.)`)}`);
|
|
39788
|
+
}
|
|
39789
|
+
if (adapter.oneTimeSetupNote) {
|
|
39790
|
+
console.log(` ${warning(adapter.oneTimeSetupNote)}`);
|
|
39791
|
+
}
|
|
39792
|
+
console.log("");
|
|
39793
|
+
}
|
|
39794
|
+
const childEnv2 = { ...process.env };
|
|
39795
|
+
if (convGuid) childEnv2.GIPITY_CONVERSATION_GUID = convGuid;
|
|
39796
|
+
const agentCmd = resolveCommand(adapter.binary);
|
|
39797
|
+
const child = spawnCommand(agentCmd, agentArgs, {
|
|
39798
|
+
stdio: "inherit",
|
|
39799
|
+
cwd: process.cwd(),
|
|
39800
|
+
env: childEnv2
|
|
39801
|
+
});
|
|
39802
|
+
child.on("error", (err) => {
|
|
39803
|
+
console.error(`
|
|
39804
|
+
${error(err.code === "ENOENT" ? `${adapter.displayName} not found. Install it: ${adapter.installHint}` : `Failed to launch ${adapter.displayName}: ${err.message}`)}`);
|
|
39805
|
+
process.exit(1);
|
|
39806
|
+
});
|
|
39807
|
+
child.on("exit", (code) => {
|
|
39808
|
+
if (syntheticSid && convGuid) {
|
|
39809
|
+
flushHeadlessCapture(adapter, syntheticSid, convGuid);
|
|
39810
|
+
}
|
|
39811
|
+
const doneLine = `Done (${formatElapsed2(Date.now() - runStart)})`;
|
|
39812
|
+
if (nonInteractive) process.stderr.write(`
|
|
39813
|
+
${doneLine}
|
|
39814
|
+
|
|
39815
|
+
`);
|
|
39816
|
+
else {
|
|
39817
|
+
console.log(`
|
|
39818
|
+
${doneLine}`);
|
|
39819
|
+
console.log(` ${muted(`Next time: gipity build from anywhere, or cd ${process.cwd()} && ${adapter.binary}.`)}`);
|
|
39820
|
+
}
|
|
39821
|
+
process.exit(code ?? 0);
|
|
39822
|
+
});
|
|
39823
|
+
}
|
|
39824
|
+
function flushHeadlessCapture(adapter, sessionId, convGuid) {
|
|
39825
|
+
const runner = join17(__clDir, "..", "hooks", "capture-runner.js");
|
|
39826
|
+
if (!existsSync14(runner)) return;
|
|
39827
|
+
const payload = JSON.stringify({ session_id: sessionId, cwd: process.cwd() });
|
|
39828
|
+
const env = { ...process.env, GIPITY_CONVERSATION_GUID: convGuid };
|
|
39829
|
+
for (const event of ["session-start", "stop"]) {
|
|
39830
|
+
try {
|
|
39831
|
+
spawnSyncCommand(process.execPath, [runner, adapter.key, event], {
|
|
39832
|
+
input: payload,
|
|
39833
|
+
env,
|
|
39834
|
+
timeout: 6e4,
|
|
39835
|
+
stdio: ["pipe", "ignore", "ignore"]
|
|
39836
|
+
});
|
|
39837
|
+
} catch {
|
|
39838
|
+
}
|
|
39839
|
+
}
|
|
39840
|
+
}
|
|
39199
39841
|
async function pickOrCreateProject(projects, existingSlugs) {
|
|
39200
39842
|
const filtered = projects.filter((p) => !p.is_default);
|
|
39201
39843
|
const cwd = process.cwd();
|
|
@@ -39256,9 +39898,9 @@ async function pickFromAll(filtered) {
|
|
|
39256
39898
|
function formatNewProjectLabel(existingSlugs) {
|
|
39257
39899
|
const slug = suggestProjectName(existingSlugs);
|
|
39258
39900
|
const root = getProjectsRoot();
|
|
39259
|
-
const home =
|
|
39260
|
-
const display2 = root === home || root.startsWith(home +
|
|
39261
|
-
return `${display2}${
|
|
39901
|
+
const home = homedir12();
|
|
39902
|
+
const display2 = root === home || root.startsWith(home + sep5) ? "~" + root.slice(home.length) : root;
|
|
39903
|
+
return `${display2}${sep5}${slug}`;
|
|
39262
39904
|
}
|
|
39263
39905
|
async function confirmAdoptCwd(cwd) {
|
|
39264
39906
|
process.stdout.write(` ${muted("Scanning directory...")} `);
|
|
@@ -40919,11 +41561,11 @@ jobCommand.command("cancel <runGuid>").description("Cancel a queued or running j
|
|
|
40919
41561
|
}
|
|
40920
41562
|
}));
|
|
40921
41563
|
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:
|
|
41564
|
+
const { existsSync: existsSync24, statSync: statSync10 } = await import("node:fs");
|
|
41565
|
+
const { resolve: resolve20, join: join26 } = await import("node:path");
|
|
40924
41566
|
const { spawnCommand: spawnCommand2, spawnSyncCommand: spawnSyncCommand2 } = await Promise.resolve().then(() => (init_platform(), platform_exports));
|
|
40925
|
-
const jobDir =
|
|
40926
|
-
if (!
|
|
41567
|
+
const jobDir = resolve20(process.cwd(), "jobs", name);
|
|
41568
|
+
if (!existsSync24(jobDir) || !statSync10(jobDir).isDirectory()) {
|
|
40927
41569
|
console.error(error(`jobs/${name}/ not found in current directory`));
|
|
40928
41570
|
process.exit(1);
|
|
40929
41571
|
}
|
|
@@ -40944,7 +41586,7 @@ jobCommand.command("run-local <name>").description("Run the job in a local Docke
|
|
|
40944
41586
|
},
|
|
40945
41587
|
{ file: "main.sh", runtime: "bash", entry: "bash" }
|
|
40946
41588
|
];
|
|
40947
|
-
const picked = variants.find((v7) =>
|
|
41589
|
+
const picked = variants.find((v7) => existsSync24(join26(jobDir, v7.file)));
|
|
40948
41590
|
if (!picked) {
|
|
40949
41591
|
console.error(error(`No main.py / main.js / main.sh found under jobs/${name}/`));
|
|
40950
41592
|
process.exit(1);
|
|
@@ -40969,7 +41611,7 @@ jobCommand.command("run-local <name>").description("Run the job in a local Docke
|
|
|
40969
41611
|
"-v",
|
|
40970
41612
|
`${jobDir}:/work`
|
|
40971
41613
|
];
|
|
40972
|
-
const needsDeps = opts.deps !== false && picked.depsFile && picked.installCmd &&
|
|
41614
|
+
const needsDeps = opts.deps !== false && picked.depsFile && picked.installCmd && existsSync24(join26(jobDir, picked.depsFile));
|
|
40973
41615
|
const handlerCmd = `${picked.entry} /work/${picked.file}`;
|
|
40974
41616
|
let shellCmd;
|
|
40975
41617
|
if (needsDeps && picked.installCmd) {
|
|
@@ -41144,7 +41786,7 @@ emailCommand.command("log").description("Recent email() sends from your app").op
|
|
|
41144
41786
|
init_api();
|
|
41145
41787
|
init_config();
|
|
41146
41788
|
init_colors();
|
|
41147
|
-
import { mkdirSync as
|
|
41789
|
+
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync13 } from "fs";
|
|
41148
41790
|
import { resolve as resolvePath2, dirname as dirname11, relative as relative5, isAbsolute } from "path";
|
|
41149
41791
|
|
|
41150
41792
|
// src/provider-docs.ts
|
|
@@ -41165,7 +41807,7 @@ async function downloadFile(url, filename) {
|
|
|
41165
41807
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
41166
41808
|
const outPath = correctExtension(filename, buffer);
|
|
41167
41809
|
ensureOutputDir(outPath);
|
|
41168
|
-
|
|
41810
|
+
writeFileSync13(outPath, buffer);
|
|
41169
41811
|
const savedPath = resolvePath2(outPath);
|
|
41170
41812
|
await pushGenerated(savedPath);
|
|
41171
41813
|
return savedPath;
|
|
@@ -41201,7 +41843,7 @@ function correctExtension(filename, buf) {
|
|
|
41201
41843
|
function ensureOutputDir(output) {
|
|
41202
41844
|
const dir = dirname11(resolvePath2(output));
|
|
41203
41845
|
try {
|
|
41204
|
-
|
|
41846
|
+
mkdirSync13(dir, { recursive: true });
|
|
41205
41847
|
} catch (err) {
|
|
41206
41848
|
throw new Error(`can't create the output directory ${dir} (${err.code || err.message}). Pick a different -o path.`);
|
|
41207
41849
|
}
|
|
@@ -41434,14 +42076,14 @@ var generateCommand = new Command("generate").description("Generate images, vide
|
|
|
41434
42076
|
init_api();
|
|
41435
42077
|
init_config();
|
|
41436
42078
|
init_colors();
|
|
41437
|
-
import { existsSync as
|
|
41438
|
-
import { join as
|
|
42079
|
+
import { existsSync as existsSync15, readFileSync as readFileSync18 } from "fs";
|
|
42080
|
+
import { join as join18 } from "path";
|
|
41439
42081
|
function readInstalledKitReadme(name) {
|
|
41440
42082
|
const root = getProjectRoot() ?? process.cwd();
|
|
41441
|
-
const kitDir =
|
|
41442
|
-
if (!
|
|
41443
|
-
const readme =
|
|
41444
|
-
return
|
|
42083
|
+
const kitDir = join18(root, "src", "packages", name);
|
|
42084
|
+
if (!existsSync15(join18(kitDir, "package.json"))) return null;
|
|
42085
|
+
const readme = join18(kitDir, "README.md");
|
|
42086
|
+
return existsSync15(readme) ? readFileSync18(readme, "utf-8") : null;
|
|
41445
42087
|
}
|
|
41446
42088
|
var skillCommand = new Command("skill").description("Task docs - read the matching skill before building");
|
|
41447
42089
|
skillCommand.command("list").description("List skills").option("--json", "Output as JSON").action((opts) => run("List", async () => {
|
|
@@ -41614,7 +42256,7 @@ var domainCommand = new Command("domain").description("Manage custom domains").a
|
|
|
41614
42256
|
}));
|
|
41615
42257
|
|
|
41616
42258
|
// src/commands/realtime.ts
|
|
41617
|
-
import { existsSync as
|
|
42259
|
+
import { existsSync as existsSync16 } from "fs";
|
|
41618
42260
|
import { dirname as dirname12, resolve as resolve15 } from "path";
|
|
41619
42261
|
init_api();
|
|
41620
42262
|
init_config();
|
|
@@ -41622,7 +42264,7 @@ init_colors();
|
|
|
41622
42264
|
function hasDeployManifest() {
|
|
41623
42265
|
const cfgPath = getConfigPath();
|
|
41624
42266
|
if (!cfgPath) return false;
|
|
41625
|
-
return
|
|
42267
|
+
return existsSync16(resolve15(dirname12(cfgPath), "gipity.yaml"));
|
|
41626
42268
|
}
|
|
41627
42269
|
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
42270
|
const config = requireConfig();
|
|
@@ -41798,7 +42440,7 @@ async function pollTestStatus(projectGuid, runGuid, opts) {
|
|
|
41798
42440
|
}
|
|
41799
42441
|
}
|
|
41800
42442
|
}
|
|
41801
|
-
await new Promise((
|
|
42443
|
+
await new Promise((resolve20) => setTimeout(resolve20, getPollInterval()));
|
|
41802
42444
|
}
|
|
41803
42445
|
}
|
|
41804
42446
|
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 () => {
|
|
@@ -42008,21 +42650,21 @@ var locationCommand = new Command("location").description("Show location").argum
|
|
|
42008
42650
|
}));
|
|
42009
42651
|
|
|
42010
42652
|
// src/commands/doctor.ts
|
|
42011
|
-
import { existsSync as
|
|
42012
|
-
import { join as
|
|
42013
|
-
import { homedir as
|
|
42653
|
+
import { existsSync as existsSync18, readFileSync as readFileSync20, statSync as statSync8 } from "fs";
|
|
42654
|
+
import { join as join20, resolve as resolve16 } from "path";
|
|
42655
|
+
import { homedir as homedir14 } from "os";
|
|
42014
42656
|
|
|
42015
42657
|
// 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 =
|
|
42658
|
+
import { readFileSync as readFileSync19, writeFileSync as writeFileSync14, mkdirSync as mkdirSync14, existsSync as existsSync17 } from "fs";
|
|
42659
|
+
import { join as join19 } from "path";
|
|
42660
|
+
import { homedir as homedir13 } from "os";
|
|
42661
|
+
var GIPITY_DIR = join19(homedir13(), ".gipity");
|
|
42662
|
+
var LOCAL_DIR = join19(GIPITY_DIR, "local");
|
|
42663
|
+
var LOCAL_PKG_DIR = join19(LOCAL_DIR, "node_modules", "gipity");
|
|
42664
|
+
var LOCAL_ENTRY = join19(LOCAL_PKG_DIR, "dist", "index.js");
|
|
42665
|
+
var STATE_FILE = join19(GIPITY_DIR, "update-state.json");
|
|
42666
|
+
var SETTINGS_FILE = join19(GIPITY_DIR, "settings.json");
|
|
42667
|
+
var UPDATE_LOG = join19(GIPITY_DIR, "update.log");
|
|
42026
42668
|
var DEFAULT_STATE = {
|
|
42027
42669
|
installedVersion: null,
|
|
42028
42670
|
lastCheckAt: 0,
|
|
@@ -42033,24 +42675,24 @@ var DEFAULT_SETTINGS = {
|
|
|
42033
42675
|
autoUpdates: true
|
|
42034
42676
|
};
|
|
42035
42677
|
function ensureDir() {
|
|
42036
|
-
|
|
42678
|
+
mkdirSync14(GIPITY_DIR, { recursive: true });
|
|
42037
42679
|
}
|
|
42038
42680
|
function readState() {
|
|
42039
|
-
if (!
|
|
42681
|
+
if (!existsSync17(STATE_FILE)) return { ...DEFAULT_STATE };
|
|
42040
42682
|
try {
|
|
42041
|
-
return { ...DEFAULT_STATE, ...JSON.parse(
|
|
42683
|
+
return { ...DEFAULT_STATE, ...JSON.parse(readFileSync19(STATE_FILE, "utf-8")) };
|
|
42042
42684
|
} catch {
|
|
42043
42685
|
return { ...DEFAULT_STATE };
|
|
42044
42686
|
}
|
|
42045
42687
|
}
|
|
42046
42688
|
function writeState(state) {
|
|
42047
42689
|
ensureDir();
|
|
42048
|
-
|
|
42690
|
+
writeFileSync14(STATE_FILE, JSON.stringify(state, null, 2));
|
|
42049
42691
|
}
|
|
42050
42692
|
function readSettings() {
|
|
42051
|
-
if (!
|
|
42693
|
+
if (!existsSync17(SETTINGS_FILE)) return { ...DEFAULT_SETTINGS };
|
|
42052
42694
|
try {
|
|
42053
|
-
return { ...DEFAULT_SETTINGS, ...JSON.parse(
|
|
42695
|
+
return { ...DEFAULT_SETTINGS, ...JSON.parse(readFileSync19(SETTINGS_FILE, "utf-8")) };
|
|
42054
42696
|
} catch {
|
|
42055
42697
|
return { ...DEFAULT_SETTINGS };
|
|
42056
42698
|
}
|
|
@@ -42066,7 +42708,7 @@ function updatesDisabled() {
|
|
|
42066
42708
|
init_colors();
|
|
42067
42709
|
init_auth();
|
|
42068
42710
|
var NODE_MIN_MAJOR = 18;
|
|
42069
|
-
function versionManagerNode(execPath, env = process.env, home =
|
|
42711
|
+
function versionManagerNode(execPath, env = process.env, home = homedir14()) {
|
|
42070
42712
|
const norm = (s) => s.replace(/\\/g, "/").replace(/\/+$/, "") + "/";
|
|
42071
42713
|
const p = norm(execPath);
|
|
42072
42714
|
const h = norm(home);
|
|
@@ -42083,10 +42725,10 @@ function versionManagerNode(execPath, env = process.env, home = homedir13()) {
|
|
|
42083
42725
|
return null;
|
|
42084
42726
|
}
|
|
42085
42727
|
function localVersion() {
|
|
42086
|
-
const pkgPath =
|
|
42087
|
-
if (!
|
|
42728
|
+
const pkgPath = join20(LOCAL_PKG_DIR, "package.json");
|
|
42729
|
+
if (!existsSync18(pkgPath)) return null;
|
|
42088
42730
|
try {
|
|
42089
|
-
return JSON.parse(
|
|
42731
|
+
return JSON.parse(readFileSync20(pkgPath, "utf-8")).version ?? null;
|
|
42090
42732
|
} catch {
|
|
42091
42733
|
return null;
|
|
42092
42734
|
}
|
|
@@ -42094,7 +42736,7 @@ function localVersion() {
|
|
|
42094
42736
|
function shimVersion() {
|
|
42095
42737
|
try {
|
|
42096
42738
|
const url = new URL("../../package.json", import.meta.url);
|
|
42097
|
-
return JSON.parse(
|
|
42739
|
+
return JSON.parse(readFileSync20(url, "utf-8")).version;
|
|
42098
42740
|
} catch {
|
|
42099
42741
|
return "unknown";
|
|
42100
42742
|
}
|
|
@@ -42109,7 +42751,7 @@ function rel(t) {
|
|
|
42109
42751
|
}
|
|
42110
42752
|
function relayAutostartInstalled() {
|
|
42111
42753
|
try {
|
|
42112
|
-
return
|
|
42754
|
+
return existsSync18(planFor({ cliPath: resolveCliPath() }).path);
|
|
42113
42755
|
} catch (err) {
|
|
42114
42756
|
if (err instanceof UnsupportedPlatformError) return null;
|
|
42115
42757
|
return null;
|
|
@@ -42147,7 +42789,7 @@ function gatherEnv(opts = {}) {
|
|
|
42147
42789
|
const cli = {
|
|
42148
42790
|
shim_version: shimVersion(),
|
|
42149
42791
|
local_version: localVersion(),
|
|
42150
|
-
local_install_ok:
|
|
42792
|
+
local_install_ok: existsSync18(LOCAL_ENTRY),
|
|
42151
42793
|
auto_updates: !dis.disabled,
|
|
42152
42794
|
updates_disabled_reason: dis.disabled ? dis.reason ?? null : null,
|
|
42153
42795
|
last_check_at: updState.lastCheckAt,
|
|
@@ -42177,12 +42819,15 @@ var doctorCommand = new Command("doctor").description("Check install + environme
|
|
|
42177
42819
|
console.log(`${muted("claude code ")} installed ${yn(env.claude.installed)} \xB7 authenticated ${yn(env.claude.authenticated)}`);
|
|
42178
42820
|
const autostartLabel = env.relay.autostart === null ? muted("n/a") : yn(env.relay.autostart);
|
|
42179
42821
|
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
|
-
|
|
42822
|
+
if (existsSync18(resolve16(process.cwd(), ".codex", "hooks.json"))) {
|
|
42823
|
+
console.log(`${muted("codex hooks ")} ${warning("written")} - session capture needs a one-time approval: run /hooks inside Codex in this project`);
|
|
42824
|
+
}
|
|
42825
|
+
console.log(`${muted("ready ")} ${env.ready ? success("yes") : warning("no - run `gipity build` (or the desktop app) to finish setup")}`);
|
|
42181
42826
|
const state = readState();
|
|
42182
42827
|
const settings = readSettings();
|
|
42183
42828
|
const dis = updatesDisabled();
|
|
42184
42829
|
const local = localVersion();
|
|
42185
|
-
const localOk =
|
|
42830
|
+
const localOk = existsSync18(LOCAL_ENTRY);
|
|
42186
42831
|
console.log("");
|
|
42187
42832
|
console.log(bold("CLI install"));
|
|
42188
42833
|
console.log(`${muted("shim version ")} ${shimVersion()}`);
|
|
@@ -42190,17 +42835,17 @@ var doctorCommand = new Command("doctor").description("Check install + environme
|
|
|
42190
42835
|
console.log(`${muted("local install ")} ${LOCAL_PKG_DIR}`);
|
|
42191
42836
|
console.log("");
|
|
42192
42837
|
console.log(`${muted("auto-updates ")} ${dis.disabled ? warning(`disabled (${dis.reason})`) : success("enabled")}`);
|
|
42193
|
-
console.log(`${muted("settings file ")} ${
|
|
42838
|
+
console.log(`${muted("settings file ")} ${existsSync18(SETTINGS_FILE) ? SETTINGS_FILE : dim("(default)")} autoUpdates=${settings.autoUpdates}`);
|
|
42194
42839
|
console.log(`${muted("last check ")} ${rel(state.lastCheckAt)}`);
|
|
42195
42840
|
console.log(`${muted("last error ")} ${state.lastError ? error(state.lastError) : dim("none")}`);
|
|
42196
|
-
console.log(`${muted("state file ")} ${
|
|
42197
|
-
console.log(`${muted("update log ")} ${
|
|
42841
|
+
console.log(`${muted("state file ")} ${existsSync18(STATE_FILE) ? STATE_FILE : dim("(none yet)")}`);
|
|
42842
|
+
console.log(`${muted("update log ")} ${existsSync18(UPDATE_LOG) ? `${UPDATE_LOG} (${statSync8(UPDATE_LOG).size} bytes)` : dim("(none yet)")}`);
|
|
42198
42843
|
console.log("");
|
|
42199
42844
|
console.log(dim("Force an update with: gipity update"));
|
|
42200
42845
|
});
|
|
42201
42846
|
|
|
42202
42847
|
// src/updater/check.ts
|
|
42203
|
-
import { appendFileSync, existsSync as
|
|
42848
|
+
import { appendFileSync, existsSync as existsSync19 } from "fs";
|
|
42204
42849
|
init_platform();
|
|
42205
42850
|
var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
|
|
42206
42851
|
function log(line) {
|
|
@@ -42235,7 +42880,7 @@ function installVersion(version) {
|
|
|
42235
42880
|
stdio: "ignore"
|
|
42236
42881
|
});
|
|
42237
42882
|
if (res.error) log(`npm spawn failed: ${res.error.message}`);
|
|
42238
|
-
return res.status === 0 &&
|
|
42883
|
+
return res.status === 0 && existsSync19(LOCAL_ENTRY);
|
|
42239
42884
|
}
|
|
42240
42885
|
async function runCheck(opts = {}) {
|
|
42241
42886
|
const state = readState();
|
|
@@ -42309,17 +42954,17 @@ var updateCommand = new Command("update").description("Update the CLI").action(a
|
|
|
42309
42954
|
init_api();
|
|
42310
42955
|
init_utils();
|
|
42311
42956
|
init_colors();
|
|
42312
|
-
import { existsSync as
|
|
42957
|
+
import { existsSync as existsSync22, readFileSync as readFileSync23, unlinkSync as unlinkSync7 } from "fs";
|
|
42313
42958
|
import { spawn as spawn2 } from "child_process";
|
|
42314
42959
|
|
|
42315
42960
|
// src/relay/daemon.ts
|
|
42316
42961
|
init_platform();
|
|
42317
42962
|
init_config();
|
|
42318
|
-
import { appendFileSync as appendFileSync3, mkdirSync as
|
|
42963
|
+
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
42964
|
import { stat, readFile } from "fs/promises";
|
|
42320
42965
|
import { createInterface as createInterface2 } from "readline";
|
|
42321
|
-
import { homedir as
|
|
42322
|
-
import { join as
|
|
42966
|
+
import { homedir as homedir16, hostname as hostname5, platform as osPlatform3, loadavg as loadavg2, freemem as freemem2, totalmem as totalmem2, cpus as cpus2 } from "os";
|
|
42967
|
+
import { join as join23 } from "path";
|
|
42323
42968
|
init_auth();
|
|
42324
42969
|
init_api();
|
|
42325
42970
|
|
|
@@ -43025,7 +43670,7 @@ var DeltaBatcher = class {
|
|
|
43025
43670
|
};
|
|
43026
43671
|
|
|
43027
43672
|
// src/relay/daemon.ts
|
|
43028
|
-
import { randomUUID } from "crypto";
|
|
43673
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
43029
43674
|
|
|
43030
43675
|
// src/relay/agent-token.ts
|
|
43031
43676
|
init_api();
|
|
@@ -43074,22 +43719,22 @@ async function revokeRelayAgentToken() {
|
|
|
43074
43719
|
// src/relay/diagnostics.ts
|
|
43075
43720
|
init_platform();
|
|
43076
43721
|
init_client_context();
|
|
43077
|
-
import { cpus, freemem, totalmem, loadavg, release, uptime, homedir as
|
|
43078
|
-
import { statfsSync, readdirSync as
|
|
43079
|
-
import { join as
|
|
43722
|
+
import { cpus, freemem, totalmem, loadavg, release, uptime, homedir as homedir15 } from "os";
|
|
43723
|
+
import { statfsSync, readdirSync as readdirSync9 } from "fs";
|
|
43724
|
+
import { join as join21 } from "path";
|
|
43080
43725
|
var PROBE_TIMEOUT_MS = 4e3;
|
|
43081
43726
|
function parseVersion(out) {
|
|
43082
43727
|
const m = out.match(/\d+\.\d+(?:\.\d+)?(?:[-.\w]*)?/);
|
|
43083
43728
|
return m ? m[0] : void 0;
|
|
43084
43729
|
}
|
|
43085
43730
|
function runProbe(cmd, args) {
|
|
43086
|
-
return new Promise((
|
|
43731
|
+
return new Promise((resolve20) => {
|
|
43087
43732
|
let out = "";
|
|
43088
43733
|
let settled = false;
|
|
43089
43734
|
const done = (s) => {
|
|
43090
43735
|
if (!settled) {
|
|
43091
43736
|
settled = true;
|
|
43092
|
-
|
|
43737
|
+
resolve20(s);
|
|
43093
43738
|
}
|
|
43094
43739
|
};
|
|
43095
43740
|
let child;
|
|
@@ -43143,12 +43788,12 @@ async function detectGpu() {
|
|
|
43143
43788
|
function diskUsage() {
|
|
43144
43789
|
try {
|
|
43145
43790
|
if (typeof statfsSync !== "function") return void 0;
|
|
43146
|
-
const st2 = statfsSync(
|
|
43791
|
+
const st2 = statfsSync(join21(homedir15(), "GipityProjects"), { bigint: false });
|
|
43147
43792
|
if (!st2 || !st2.bsize) return void 0;
|
|
43148
43793
|
return { total: st2.bsize * st2.blocks, free: st2.bsize * st2.bavail };
|
|
43149
43794
|
} catch {
|
|
43150
43795
|
try {
|
|
43151
|
-
const st2 = statfsSync(
|
|
43796
|
+
const st2 = statfsSync(homedir15(), { bigint: false });
|
|
43152
43797
|
return { total: st2.bsize * st2.blocks, free: st2.bsize * st2.bavail };
|
|
43153
43798
|
} catch {
|
|
43154
43799
|
return void 0;
|
|
@@ -43157,7 +43802,7 @@ function diskUsage() {
|
|
|
43157
43802
|
}
|
|
43158
43803
|
function localProjectCount() {
|
|
43159
43804
|
try {
|
|
43160
|
-
return
|
|
43805
|
+
return readdirSync9(join21(homedir15(), "GipityProjects"), { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).length;
|
|
43161
43806
|
} catch {
|
|
43162
43807
|
return void 0;
|
|
43163
43808
|
}
|
|
@@ -43170,15 +43815,17 @@ async function collectDiagnostics() {
|
|
|
43170
43815
|
return [];
|
|
43171
43816
|
}
|
|
43172
43817
|
})();
|
|
43173
|
-
const [claude, codex, cursor, gpu] = await Promise.all([
|
|
43818
|
+
const [claude, codex, grok, cursor, gpu] = await Promise.all([
|
|
43174
43819
|
probeVersion("claude"),
|
|
43175
43820
|
probeVersion("codex"),
|
|
43821
|
+
probeVersion("grok"),
|
|
43176
43822
|
probeVersion("cursor"),
|
|
43177
43823
|
detectGpu()
|
|
43178
43824
|
]);
|
|
43179
43825
|
const agents = {};
|
|
43180
43826
|
if (claude) agents.claude_code = claude;
|
|
43181
43827
|
if (codex) agents.codex = codex;
|
|
43828
|
+
if (grok) agents.grok = grok;
|
|
43182
43829
|
if (cursor) agents.cursor = cursor;
|
|
43183
43830
|
return {
|
|
43184
43831
|
collected_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -43286,8 +43933,8 @@ var SessionPool = class {
|
|
|
43286
43933
|
const done = new Promise((r) => {
|
|
43287
43934
|
markDone = r;
|
|
43288
43935
|
});
|
|
43289
|
-
return new Promise((
|
|
43290
|
-
const turn = { onMessage: p.onMessage, resolve:
|
|
43936
|
+
return new Promise((resolve20, reject) => {
|
|
43937
|
+
const turn = { onMessage: p.onMessage, resolve: resolve20, reject, interruptRequested: false, wasHot, done, markDone };
|
|
43291
43938
|
session.current = turn;
|
|
43292
43939
|
session.state = "running";
|
|
43293
43940
|
session.lastActivityAt = Date.now();
|
|
@@ -43464,7 +44111,7 @@ var SessionPool = class {
|
|
|
43464
44111
|
// src/relay/daemon.ts
|
|
43465
44112
|
init_config();
|
|
43466
44113
|
init_api();
|
|
43467
|
-
var RELAY_LOG_PATH =
|
|
44114
|
+
var RELAY_LOG_PATH = join23(homedir16(), ".gipity", "relay.log");
|
|
43468
44115
|
var HEARTBEAT_INTERVAL_MS = parseInt(process.env.GIPITY_RELAY_HEARTBEAT_MS || "60000", 10);
|
|
43469
44116
|
var DIAGNOSTICS_INTERVAL_MS = parseInt(process.env.GIPITY_RELAY_DIAGNOSTICS_MS || String(24 * 60 * 60 * 1e3), 10);
|
|
43470
44117
|
var LONG_POLL_TIMEOUT_MS = parseInt(process.env.GIPITY_RELAY_POLL_TIMEOUT_MS || "35000", 10);
|
|
@@ -43564,7 +44211,7 @@ function lockLogPerms(dir, file) {
|
|
|
43564
44211
|
chmodSync3(dir, 448);
|
|
43565
44212
|
} catch {
|
|
43566
44213
|
}
|
|
43567
|
-
if (!
|
|
44214
|
+
if (!existsSync21(file)) {
|
|
43568
44215
|
try {
|
|
43569
44216
|
closeSync4(openSync4(file, "a", 384));
|
|
43570
44217
|
} catch {
|
|
@@ -43581,8 +44228,8 @@ function log2(level, msg, extra) {
|
|
|
43581
44228
|
const pretty = `${C7.dim(hhmmss())} ${badge(level)} ${C7.bold(msg)}${formatExtra(extra)}`;
|
|
43582
44229
|
process.stderr.write(pretty + "\n");
|
|
43583
44230
|
try {
|
|
43584
|
-
const dir =
|
|
43585
|
-
|
|
44231
|
+
const dir = join23(homedir16(), ".gipity");
|
|
44232
|
+
mkdirSync16(dir, { recursive: true });
|
|
43586
44233
|
lockLogPerms(dir, RELAY_LOG_PATH);
|
|
43587
44234
|
const json = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level, msg, ...extra ?? {} });
|
|
43588
44235
|
appendFileSync3(RELAY_LOG_PATH, json + "\n");
|
|
@@ -43706,10 +44353,10 @@ async function heartbeatLoop(ctx) {
|
|
|
43706
44353
|
}
|
|
43707
44354
|
await Promise.race([
|
|
43708
44355
|
sleep4(HEARTBEAT_INTERVAL_MS, ctx.abort.signal),
|
|
43709
|
-
new Promise((
|
|
44356
|
+
new Promise((resolve20) => {
|
|
43710
44357
|
heartbeatPoke = () => {
|
|
43711
44358
|
heartbeatPoke = null;
|
|
43712
|
-
|
|
44359
|
+
resolve20();
|
|
43713
44360
|
};
|
|
43714
44361
|
})
|
|
43715
44362
|
]);
|
|
@@ -43753,9 +44400,9 @@ async function dispatchLoop(ctx, opts) {
|
|
|
43753
44400
|
while (inflight.size >= MAX_CONCURRENT_DISPATCHES && !ctx.abort.signal.aborted) {
|
|
43754
44401
|
await Promise.race([
|
|
43755
44402
|
...inflight,
|
|
43756
|
-
new Promise((
|
|
43757
|
-
if (ctx.abort.signal.aborted) return
|
|
43758
|
-
ctx.abort.signal.addEventListener("abort", () =>
|
|
44403
|
+
new Promise((resolve20) => {
|
|
44404
|
+
if (ctx.abort.signal.aborted) return resolve20();
|
|
44405
|
+
ctx.abort.signal.addEventListener("abort", () => resolve20(), { once: true });
|
|
43759
44406
|
})
|
|
43760
44407
|
]);
|
|
43761
44408
|
}
|
|
@@ -43982,7 +44629,7 @@ function isSafeSessionId(s) {
|
|
|
43982
44629
|
function transcriptPathFor(cwd, sessionId) {
|
|
43983
44630
|
if (!isSafeSessionId(sessionId)) return null;
|
|
43984
44631
|
const slug = cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
43985
|
-
return
|
|
44632
|
+
return join23(homedir16(), ".claude", "projects", slug, `${sessionId}.jsonl`);
|
|
43986
44633
|
}
|
|
43987
44634
|
function formatDuration(ms2) {
|
|
43988
44635
|
const totalSec = ms2 / 1e3;
|
|
@@ -44051,7 +44698,7 @@ async function handleDispatch(claimed) {
|
|
|
44051
44698
|
{ onWarn: (msg, meta) => log2("warn", msg, { id: d.short_guid, ...meta }) }
|
|
44052
44699
|
);
|
|
44053
44700
|
const pushSystem = (content) => {
|
|
44054
|
-
queue.push({ kind: "system", content, ts: (/* @__PURE__ */ new Date()).toISOString(), source_uuid:
|
|
44701
|
+
queue.push({ kind: "system", content, ts: (/* @__PURE__ */ new Date()).toISOString(), source_uuid: randomUUID2() });
|
|
44055
44702
|
};
|
|
44056
44703
|
const flushQueue = async () => {
|
|
44057
44704
|
await queue.close(3e4);
|
|
@@ -44097,11 +44744,16 @@ async function handleDispatch(claimed) {
|
|
|
44097
44744
|
}
|
|
44098
44745
|
if (!syncFailed) pushSystem(bootstrapped ? "Project files synced." : "Attached files synced.");
|
|
44099
44746
|
}
|
|
44747
|
+
const adapter = getAdapterBySource(d.remote_type ?? "claude_code");
|
|
44748
|
+
if (adapter.key !== "claude") {
|
|
44749
|
+
await handleNonClaudeDispatch(adapter, d, cwd, queue, pushSystem, flushQueue);
|
|
44750
|
+
return;
|
|
44751
|
+
}
|
|
44100
44752
|
if (SESSION_POOL_ENABLED) {
|
|
44101
44753
|
const handled = await tryHandleViaPool(d, cwd, queue, pushSystem, flushQueue);
|
|
44102
44754
|
if (handled) return;
|
|
44103
44755
|
}
|
|
44104
|
-
const args = ["claude", "-p", d.message, "--permission-mode", "bypassPermissions"];
|
|
44756
|
+
const args = ["build", "--agent", "claude", "-p", d.message, "--permission-mode", "bypassPermissions"];
|
|
44105
44757
|
args.push("--append-system-prompt", GIPITY_QUESTION_PROTOCOL);
|
|
44106
44758
|
if (d.model) {
|
|
44107
44759
|
args.push("--model", d.model);
|
|
@@ -44109,7 +44761,7 @@ async function handleDispatch(claimed) {
|
|
|
44109
44761
|
if (d.kind === "resume" && d.remote_session_id) {
|
|
44110
44762
|
args.push("--resume", d.remote_session_id);
|
|
44111
44763
|
}
|
|
44112
|
-
log2("debug", "spawning gipity claude", {
|
|
44764
|
+
log2("debug", "spawning gipity build --agent claude", {
|
|
44113
44765
|
id: d.short_guid,
|
|
44114
44766
|
cwd,
|
|
44115
44767
|
args,
|
|
@@ -44158,8 +44810,8 @@ async function handleDispatch(claimed) {
|
|
|
44158
44810
|
const header = `Running Claude Code - ${counts.join(" + ")} words${resumeNote}`;
|
|
44159
44811
|
const ts2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
44160
44812
|
queue.push(
|
|
44161
|
-
{ kind: "prompt", prompt: d.message, ts: ts2, source_uuid:
|
|
44162
|
-
{ kind: "system", content: header, ts: ts2, source_uuid:
|
|
44813
|
+
{ kind: "prompt", prompt: d.message, ts: ts2, source_uuid: randomUUID2() },
|
|
44814
|
+
{ kind: "system", content: header, ts: ts2, source_uuid: randomUUID2() }
|
|
44163
44815
|
);
|
|
44164
44816
|
const t0 = Date.now();
|
|
44165
44817
|
let exitCode = 1;
|
|
@@ -44206,7 +44858,61 @@ async function handleDispatch(claimed) {
|
|
|
44206
44858
|
await ack(d.short_guid, "done", void 0, metrics);
|
|
44207
44859
|
} else {
|
|
44208
44860
|
log2("warn", "dispatch child exited nonzero", { id: d.short_guid, exitCode, ms: ms2 });
|
|
44209
|
-
await ack(d.short_guid, "error", `
|
|
44861
|
+
await ack(d.short_guid, "error", `Claude Code exited with code ${exitCode}${stderrNote}`);
|
|
44862
|
+
}
|
|
44863
|
+
}
|
|
44864
|
+
async function handleNonClaudeDispatch(adapter, d, cwd, queue, pushSystem, flushQueue) {
|
|
44865
|
+
const args = ["build", "--agent", adapter.key, "-p", d.message, "--bypass-approvals"];
|
|
44866
|
+
if (d.model) args.push("--model", d.model);
|
|
44867
|
+
if (d.kind === "resume" && d.remote_session_id) args.push("--resume", d.remote_session_id);
|
|
44868
|
+
log2("debug", `spawning gipity build --agent ${adapter.key}`, {
|
|
44869
|
+
id: d.short_guid,
|
|
44870
|
+
cwd,
|
|
44871
|
+
args,
|
|
44872
|
+
conv: d.conversation_guid,
|
|
44873
|
+
chain: d.kind === "resume" ? `resume ${d.remote_session_id}` : "start (fresh session)"
|
|
44874
|
+
});
|
|
44875
|
+
const words = d.message.trim().split(/\s+/).filter(Boolean).length;
|
|
44876
|
+
pushSystem(`Running ${adapter.displayName} - ${words.toLocaleString("en-US")} words`);
|
|
44877
|
+
const t0 = Date.now();
|
|
44878
|
+
let exitCode = 1;
|
|
44879
|
+
let spawnErr = null;
|
|
44880
|
+
let killed = false;
|
|
44881
|
+
let runtimeLimit = false;
|
|
44882
|
+
let stderrTail = "";
|
|
44883
|
+
try {
|
|
44884
|
+
const result = await spawnGipityClaude(args, cwd, d, queue, { streamJson: false });
|
|
44885
|
+
exitCode = result.exitCode;
|
|
44886
|
+
killed = result.killed;
|
|
44887
|
+
runtimeLimit = result.runtimeLimit ?? false;
|
|
44888
|
+
stderrTail = result.stderrTail ?? "";
|
|
44889
|
+
} catch (err) {
|
|
44890
|
+
spawnErr = err?.message || String(err);
|
|
44891
|
+
log2("error", "dispatch spawn failed", { id: d.short_guid, err: spawnErr });
|
|
44892
|
+
}
|
|
44893
|
+
const dur = formatDuration(Date.now() - t0);
|
|
44894
|
+
if (!spawnErr && !killed) {
|
|
44895
|
+
try {
|
|
44896
|
+
await spawnSync2(cwd, PROJECT_SYNC_TIMEOUT_MS);
|
|
44897
|
+
} catch (err) {
|
|
44898
|
+
log2("warn", "sync after dispatch failed", { id: d.short_guid, err: err?.message });
|
|
44899
|
+
}
|
|
44900
|
+
}
|
|
44901
|
+
const stderrNote = stderrTail ? `: ${stderrTail.slice(0, 300)}` : "";
|
|
44902
|
+
const tail = runtimeLimit ? `stopped after ${dur} (runtime limit)` : killed ? `cancelled (${dur})` : spawnErr ? `failed (${dur}: ${spawnErr})` : exitCode === 0 ? `finished (${dur})` : `failed (${dur}, exit ${exitCode}${stderrNote})`;
|
|
44903
|
+
pushSystem(`${adapter.displayName} ${tail}`);
|
|
44904
|
+
await flushQueue();
|
|
44905
|
+
if (runtimeLimit) {
|
|
44906
|
+
await ack(d.short_guid, "error", `${adapter.displayName} stopped after ${dur} (runtime limit)`);
|
|
44907
|
+
} else if (killed) {
|
|
44908
|
+
await ack(d.short_guid, "cancelled");
|
|
44909
|
+
} else if (spawnErr) {
|
|
44910
|
+
await ack(d.short_guid, "error", spawnErr);
|
|
44911
|
+
} else if (exitCode === 0) {
|
|
44912
|
+
log2("info", "dispatch done", { id: d.short_guid, agent: adapter.key });
|
|
44913
|
+
await ack(d.short_guid, "done");
|
|
44914
|
+
} else {
|
|
44915
|
+
await ack(d.short_guid, "error", `${adapter.displayName} exited with code ${exitCode}${stderrNote}`);
|
|
44210
44916
|
}
|
|
44211
44917
|
}
|
|
44212
44918
|
async function wrapPoolMessage(d, cwd, resume) {
|
|
@@ -44248,8 +44954,8 @@ async function tryHandleViaPool(d, cwd, queue, pushSystem, flushQueue) {
|
|
|
44248
44954
|
const words = d.message.trim().split(/\s+/).filter(Boolean).length;
|
|
44249
44955
|
const ts0 = (/* @__PURE__ */ new Date()).toISOString();
|
|
44250
44956
|
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:
|
|
44957
|
+
{ kind: "prompt", prompt: d.message, ts: ts0, source_uuid: randomUUID2() },
|
|
44958
|
+
{ kind: "system", content: `Running Claude Code - ${words.toLocaleString("en-US")} words${wasLive ? " (hot session)" : ""}`, ts: ts0, source_uuid: randomUUID2() }
|
|
44253
44959
|
);
|
|
44254
44960
|
const phases = new PhaseTracker();
|
|
44255
44961
|
const deltaAcc = new DeltaAccumulator(getRelaySecrets);
|
|
@@ -44304,7 +45010,7 @@ async function tryHandleViaPool(d, cwd, queue, pushSystem, flushQueue) {
|
|
|
44304
45010
|
const ts2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
44305
45011
|
for (const e of entries) {
|
|
44306
45012
|
e.ts = ts2;
|
|
44307
|
-
if (!e.source_uuid) e.source_uuid =
|
|
45013
|
+
if (!e.source_uuid) e.source_uuid = randomUUID2();
|
|
44308
45014
|
}
|
|
44309
45015
|
queue.push(...entries);
|
|
44310
45016
|
};
|
|
@@ -44377,11 +45083,11 @@ async function resolveCwdForProject(d) {
|
|
|
44377
45083
|
throw new Error(`Invalid project slug: ${JSON.stringify(d.project_slug)}`);
|
|
44378
45084
|
}
|
|
44379
45085
|
const root = getProjectsRoot();
|
|
44380
|
-
const path5 =
|
|
44381
|
-
const configPath =
|
|
44382
|
-
if (
|
|
45086
|
+
const path5 = join23(root, d.project_slug);
|
|
45087
|
+
const configPath = join23(path5, ".gipity.json");
|
|
45088
|
+
if (existsSync21(configPath)) {
|
|
44383
45089
|
try {
|
|
44384
|
-
const cfg = JSON.parse(
|
|
45090
|
+
const cfg = JSON.parse(readFileSync22(configPath, "utf-8"));
|
|
44385
45091
|
if (cfg.projectGuid === d.project_guid) return { cwd: path5, bootstrapped: false };
|
|
44386
45092
|
log2("warn", "project dir exists but guid mismatch - using it anyway", {
|
|
44387
45093
|
path: path5,
|
|
@@ -44393,9 +45099,9 @@ async function resolveCwdForProject(d) {
|
|
|
44393
45099
|
}
|
|
44394
45100
|
}
|
|
44395
45101
|
log2("info", "bootstrapping new project dir", { slug: d.project_slug, path: path5 });
|
|
44396
|
-
|
|
45102
|
+
mkdirSync16(path5, { recursive: true });
|
|
44397
45103
|
const apiBase2 = getApiBaseOverride() || DEFAULT_API_BASE;
|
|
44398
|
-
|
|
45104
|
+
writeFileSync15(configPath, JSON.stringify({
|
|
44399
45105
|
projectGuid: d.project_guid,
|
|
44400
45106
|
projectSlug: d.project_slug,
|
|
44401
45107
|
accountSlug: d.account_slug,
|
|
@@ -44407,10 +45113,7 @@ async function resolveCwdForProject(d) {
|
|
|
44407
45113
|
const origCwd = process.cwd();
|
|
44408
45114
|
try {
|
|
44409
45115
|
process.chdir(path5);
|
|
44410
|
-
|
|
44411
|
-
setupClaudeMd();
|
|
44412
|
-
setupAgentsMd();
|
|
44413
|
-
setupGitignore();
|
|
45116
|
+
setupProjectTools();
|
|
44414
45117
|
} finally {
|
|
44415
45118
|
process.chdir(origCwd);
|
|
44416
45119
|
}
|
|
@@ -44475,7 +45178,7 @@ async function killRunningForConv(convGuid) {
|
|
|
44475
45178
|
}
|
|
44476
45179
|
}
|
|
44477
45180
|
const graceTimers = [];
|
|
44478
|
-
const escalate = new Promise((
|
|
45181
|
+
const escalate = new Promise((resolve20) => {
|
|
44479
45182
|
const t = setTimeout(() => {
|
|
44480
45183
|
for (const e of matches) {
|
|
44481
45184
|
log2("warn", "previous dispatch ignored SIGTERM - escalating to SIGKILL", { conv: convGuid });
|
|
@@ -44484,7 +45187,7 @@ async function killRunningForConv(convGuid) {
|
|
|
44484
45187
|
} catch {
|
|
44485
45188
|
}
|
|
44486
45189
|
}
|
|
44487
|
-
|
|
45190
|
+
resolve20();
|
|
44488
45191
|
}, KILL_GRACE_MS);
|
|
44489
45192
|
graceTimers.push(t);
|
|
44490
45193
|
});
|
|
@@ -44495,7 +45198,7 @@ async function killRunningForConv(convGuid) {
|
|
|
44495
45198
|
}
|
|
44496
45199
|
async function spawnSync2(cwd, timeoutMs, onSpawn) {
|
|
44497
45200
|
const cmd = process.env.GIPITY_RELAY_CLAUDE_CMD || resolveCommand("gipity");
|
|
44498
|
-
return new Promise((
|
|
45201
|
+
return new Promise((resolve20, reject) => {
|
|
44499
45202
|
const child = spawnCommand(cmd, ["sync", "--json"], {
|
|
44500
45203
|
cwd,
|
|
44501
45204
|
env: childEnv(),
|
|
@@ -44519,7 +45222,7 @@ async function spawnSync2(cwd, timeoutMs, onSpawn) {
|
|
|
44519
45222
|
} catch {
|
|
44520
45223
|
}
|
|
44521
45224
|
try {
|
|
44522
|
-
unlinkSync6(
|
|
45225
|
+
unlinkSync6(join23(cwd, ".gipity", "sync.lock"));
|
|
44523
45226
|
} catch {
|
|
44524
45227
|
}
|
|
44525
45228
|
finish(() => reject(new Error(`timed out after ${Math.round(timeoutMs / 1e3)}s`)));
|
|
@@ -44535,7 +45238,7 @@ async function spawnSync2(cwd, timeoutMs, onSpawn) {
|
|
|
44535
45238
|
child.on("exit", (code) => finish(() => {
|
|
44536
45239
|
if (code === 0) {
|
|
44537
45240
|
log2("info", "sync done", { cwd, stdoutLen });
|
|
44538
|
-
|
|
45241
|
+
resolve20();
|
|
44539
45242
|
} else {
|
|
44540
45243
|
reject(new Error(`gipity sync exited ${code}${stderrBuf ? `: ${stderrBuf.trim().slice(0, 300)}` : ""}`));
|
|
44541
45244
|
}
|
|
@@ -44543,10 +45246,11 @@ async function spawnSync2(cwd, timeoutMs, onSpawn) {
|
|
|
44543
45246
|
});
|
|
44544
45247
|
}
|
|
44545
45248
|
async function spawnGipityClaude(args, cwd, d, queue, meta) {
|
|
45249
|
+
const streamJson = meta?.streamJson !== false;
|
|
44546
45250
|
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((
|
|
45251
|
+
const fullArgs = streamJson ? [...args, "--output-format", "stream-json", "--verbose", "--include-partial-messages"] : [...args];
|
|
45252
|
+
const env = streamJson ? childEnv({ GIPITY_CONVERSATION_GUID: d.conversation_guid, GIPITY_CAPTURE: "off" }) : childEnv({ GIPITY_CONVERSATION_GUID: d.conversation_guid });
|
|
45253
|
+
return new Promise((resolve20, reject) => {
|
|
44550
45254
|
const child = spawnCommand(cmd, fullArgs, { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
44551
45255
|
let resolveExited = () => {
|
|
44552
45256
|
};
|
|
@@ -44675,16 +45379,18 @@ async function spawnGipityClaude(args, cwd, d, queue, meta) {
|
|
|
44675
45379
|
const ts2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
44676
45380
|
for (const e of entries) {
|
|
44677
45381
|
e.ts = ts2;
|
|
44678
|
-
if (!e.source_uuid) e.source_uuid =
|
|
45382
|
+
if (!e.source_uuid) e.source_uuid = randomUUID2();
|
|
44679
45383
|
}
|
|
44680
45384
|
q.push(...entries);
|
|
44681
45385
|
});
|
|
44682
45386
|
child.stdout?.on("data", (chunk) => {
|
|
44683
45387
|
stdoutBytesTotal += chunk.length;
|
|
44684
45388
|
lastStdoutByteAt = Date.now();
|
|
44685
|
-
splitter.push(chunk);
|
|
45389
|
+
if (streamJson) splitter.push(chunk);
|
|
45390
|
+
});
|
|
45391
|
+
child.stdout?.on("end", () => {
|
|
45392
|
+
if (streamJson) splitter.flush();
|
|
44686
45393
|
});
|
|
44687
|
-
child.stdout?.on("end", () => splitter.flush());
|
|
44688
45394
|
const errPrefix = C7.dim("\u2502 ");
|
|
44689
45395
|
const stderrTail = [];
|
|
44690
45396
|
const errRl = child.stderr ? createInterface2({ input: child.stderr }) : null;
|
|
@@ -44710,7 +45416,7 @@ async function spawnGipityClaude(args, cwd, d, queue, meta) {
|
|
|
44710
45416
|
if (signal === "SIGTERM" || signal === "SIGKILL") killed = true;
|
|
44711
45417
|
if (finalResult) {
|
|
44712
45418
|
finalResult.ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
44713
|
-
finalResult.source_uuid =
|
|
45419
|
+
finalResult.source_uuid = randomUUID2();
|
|
44714
45420
|
q.push(finalResult);
|
|
44715
45421
|
}
|
|
44716
45422
|
if (unmapped.size > 0) {
|
|
@@ -44724,7 +45430,7 @@ async function spawnGipityClaude(args, cwd, d, queue, meta) {
|
|
|
44724
45430
|
void postProgress(d.conversation_guid, buildProgressPayload(false));
|
|
44725
45431
|
if (ownQueue) await q.close();
|
|
44726
45432
|
cleanup();
|
|
44727
|
-
|
|
45433
|
+
resolve20({
|
|
44728
45434
|
exitCode: code ?? 1,
|
|
44729
45435
|
killed,
|
|
44730
45436
|
runtimeLimit,
|
|
@@ -44738,10 +45444,10 @@ async function runDispatchSync(d, cwd) {
|
|
|
44738
45444
|
let killed = false;
|
|
44739
45445
|
try {
|
|
44740
45446
|
await spawnSync2(cwd, PROJECT_SYNC_TIMEOUT_MS, (child) => {
|
|
44741
|
-
const exited = new Promise((
|
|
45447
|
+
const exited = new Promise((resolve20) => {
|
|
44742
45448
|
child.once("exit", (_code, signal) => {
|
|
44743
45449
|
if (signal === "SIGTERM") killed = true;
|
|
44744
|
-
|
|
45450
|
+
resolve20();
|
|
44745
45451
|
});
|
|
44746
45452
|
});
|
|
44747
45453
|
running.set(d.short_guid, { child, convGuid: d.conversation_guid, exited });
|
|
@@ -44772,18 +45478,18 @@ function killDispatch(shortGuid) {
|
|
|
44772
45478
|
return false;
|
|
44773
45479
|
}
|
|
44774
45480
|
function sleep4(ms2, signal) {
|
|
44775
|
-
return new Promise((
|
|
44776
|
-
if (signal?.aborted) return
|
|
45481
|
+
return new Promise((resolve20) => {
|
|
45482
|
+
if (signal?.aborted) return resolve20();
|
|
44777
45483
|
let onAbort = null;
|
|
44778
45484
|
const t = setTimeout(() => {
|
|
44779
45485
|
if (onAbort && signal) signal.removeEventListener("abort", onAbort);
|
|
44780
|
-
|
|
45486
|
+
resolve20();
|
|
44781
45487
|
}, ms2);
|
|
44782
45488
|
if (signal) {
|
|
44783
45489
|
onAbort = () => {
|
|
44784
45490
|
clearTimeout(t);
|
|
44785
45491
|
signal.removeEventListener("abort", onAbort);
|
|
44786
|
-
|
|
45492
|
+
resolve20();
|
|
44787
45493
|
};
|
|
44788
45494
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
44789
45495
|
}
|
|
@@ -44796,7 +45502,7 @@ init_colors();
|
|
|
44796
45502
|
function requirePaired() {
|
|
44797
45503
|
const device = getDevice();
|
|
44798
45504
|
if (!device) {
|
|
44799
|
-
console.error(`${error("No paired device.")} Run ${bold("gipity
|
|
45505
|
+
console.error(`${error("No paired device.")} Run ${bold("gipity connect")} to pair this machine.`);
|
|
44800
45506
|
process.exit(1);
|
|
44801
45507
|
}
|
|
44802
45508
|
return device;
|
|
@@ -44937,7 +45643,7 @@ relayCommand.command("status").description("Show pairing status").option("--json
|
|
|
44937
45643
|
return;
|
|
44938
45644
|
}
|
|
44939
45645
|
if (!s.device) {
|
|
44940
|
-
console.log(`${muted("No paired device.")} Run ${brand("gipity
|
|
45646
|
+
console.log(`${muted("No paired device.")} Run ${brand("gipity connect")} to pair this machine.`);
|
|
44941
45647
|
return;
|
|
44942
45648
|
}
|
|
44943
45649
|
console.log(`${bold("Device:")} ${brand(s.device.name)} ${muted(`(${s.device.guid})`)}`);
|
|
@@ -44953,11 +45659,11 @@ relayCommand.command("run").description("Run the background service").option("-v
|
|
|
44953
45659
|
});
|
|
44954
45660
|
relayCommand.command("stop").description("Stop the background service").option("--force", "Force-stop if it doesn't exit cleanly within 5s").action(async (opts) => {
|
|
44955
45661
|
const pidPath = getDaemonPidPath();
|
|
44956
|
-
if (!
|
|
45662
|
+
if (!existsSync22(pidPath)) {
|
|
44957
45663
|
console.log(muted("Background service isn't running."));
|
|
44958
45664
|
return;
|
|
44959
45665
|
}
|
|
44960
|
-
const pid = parseInt(
|
|
45666
|
+
const pid = parseInt(readFileSync23(pidPath, "utf-8").trim(), 10);
|
|
44961
45667
|
if (!pid || isNaN(pid)) {
|
|
44962
45668
|
console.error(error("PID file is empty or malformed."));
|
|
44963
45669
|
process.exit(1);
|
|
@@ -45081,8 +45787,8 @@ relayCommand.command("revoke").description("Revoke and forget this device").acti
|
|
|
45081
45787
|
} catch {
|
|
45082
45788
|
}
|
|
45083
45789
|
const pidPath = getDaemonPidPath();
|
|
45084
|
-
if (
|
|
45085
|
-
const pid = parseInt(
|
|
45790
|
+
if (existsSync22(pidPath)) {
|
|
45791
|
+
const pid = parseInt(readFileSync23(pidPath, "utf-8").trim(), 10);
|
|
45086
45792
|
if (pid && !isNaN(pid)) {
|
|
45087
45793
|
try {
|
|
45088
45794
|
process.kill(pid, "SIGTERM");
|
|
@@ -45095,13 +45801,13 @@ relayCommand.command("revoke").description("Revoke and forget this device").acti
|
|
|
45095
45801
|
});
|
|
45096
45802
|
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
45803
|
const path5 = RELAY_LOG_PATH;
|
|
45098
|
-
if (!
|
|
45804
|
+
if (!existsSync22(path5)) {
|
|
45099
45805
|
console.log(muted("No log file yet. Start the service with `gipity relay run` (or install it)."));
|
|
45100
45806
|
return;
|
|
45101
45807
|
}
|
|
45102
45808
|
const lines = parseInt(opts.lines, 10) || 100;
|
|
45103
45809
|
try {
|
|
45104
|
-
const all =
|
|
45810
|
+
const all = readFileSync23(path5, "utf-8").split("\n");
|
|
45105
45811
|
const tail = all.slice(-lines - 1).join("\n");
|
|
45106
45812
|
process.stdout.write(tail);
|
|
45107
45813
|
} catch (err) {
|
|
@@ -45127,7 +45833,7 @@ registerInstallCommands(relayCommand);
|
|
|
45127
45833
|
function requirePaired2() {
|
|
45128
45834
|
const device = getDevice();
|
|
45129
45835
|
if (!device) {
|
|
45130
|
-
console.error(`${error("No paired device.")} Run ${brand("gipity
|
|
45836
|
+
console.error(`${error("No paired device.")} Run ${brand("gipity connect")} to pair this machine.`);
|
|
45131
45837
|
process.exit(1);
|
|
45132
45838
|
}
|
|
45133
45839
|
return device;
|
|
@@ -45136,9 +45842,9 @@ function requirePaired2() {
|
|
|
45136
45842
|
// src/commands/setup.ts
|
|
45137
45843
|
init_auth();
|
|
45138
45844
|
init_colors();
|
|
45139
|
-
var
|
|
45845
|
+
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
45846
|
try {
|
|
45141
|
-
console.log(` ${bold("Gipity
|
|
45847
|
+
console.log(` ${bold("Gipity connect")} ${muted("- let gipity.ai drive this computer")}`);
|
|
45142
45848
|
console.log("");
|
|
45143
45849
|
let auth = getAuth();
|
|
45144
45850
|
if (auth && !sessionExpired()) {
|
|
@@ -45156,9 +45862,9 @@ var setupCommand = new Command("setup").description("Set up this computer as a r
|
|
|
45156
45862
|
if (enabled) {
|
|
45157
45863
|
const running2 = isRelayEnabled() && !isPaused();
|
|
45158
45864
|
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
|
|
45865
|
+
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
45866
|
} else {
|
|
45161
|
-
console.log(` ${muted("No relay set up. Run `gipity
|
|
45867
|
+
console.log(` ${muted("No relay set up. Run `gipity connect` again anytime, or `gipity build` to start building.")}`);
|
|
45162
45868
|
}
|
|
45163
45869
|
console.log("");
|
|
45164
45870
|
} catch (err) {
|
|
@@ -45174,15 +45880,15 @@ init_api();
|
|
|
45174
45880
|
init_auth();
|
|
45175
45881
|
init_utils();
|
|
45176
45882
|
init_colors();
|
|
45177
|
-
import { existsSync as
|
|
45178
|
-
import { homedir as
|
|
45179
|
-
import { join as
|
|
45883
|
+
import { existsSync as existsSync23, rmSync as rmSync3, unlinkSync as unlinkSync8, readFileSync as readFileSync24, writeFileSync as writeFileSync16 } from "fs";
|
|
45884
|
+
import { homedir as homedir17, platform as osPlatform4 } from "os";
|
|
45885
|
+
import { join as join24, resolve as resolve18 } from "path";
|
|
45180
45886
|
function removeGipityPluginConfig() {
|
|
45181
|
-
const settingsPath =
|
|
45182
|
-
if (!
|
|
45887
|
+
const settingsPath = join24(homedir17(), ".claude", "settings.json");
|
|
45888
|
+
if (!existsSync23(settingsPath)) return false;
|
|
45183
45889
|
let settings;
|
|
45184
45890
|
try {
|
|
45185
|
-
settings = JSON.parse(
|
|
45891
|
+
settings = JSON.parse(readFileSync24(settingsPath, "utf-8"));
|
|
45186
45892
|
} catch {
|
|
45187
45893
|
return false;
|
|
45188
45894
|
}
|
|
@@ -45197,7 +45903,7 @@ function removeGipityPluginConfig() {
|
|
|
45197
45903
|
if (Object.keys(settings.extraKnownMarketplaces).length === 0) delete settings.extraKnownMarketplaces;
|
|
45198
45904
|
changed = true;
|
|
45199
45905
|
}
|
|
45200
|
-
if (changed)
|
|
45906
|
+
if (changed) writeFileSync16(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
45201
45907
|
return changed;
|
|
45202
45908
|
}
|
|
45203
45909
|
function removeInstallerPathLines() {
|
|
@@ -45205,11 +45911,11 @@ function removeInstallerPathLines() {
|
|
|
45205
45911
|
const marker = "# Added by the Gipity installer";
|
|
45206
45912
|
const touched = [];
|
|
45207
45913
|
for (const name of [".bashrc", ".zshrc", ".profile"]) {
|
|
45208
|
-
const rc2 =
|
|
45209
|
-
if (!
|
|
45914
|
+
const rc2 = join24(homedir17(), name);
|
|
45915
|
+
if (!existsSync23(rc2)) continue;
|
|
45210
45916
|
let text;
|
|
45211
45917
|
try {
|
|
45212
|
-
text =
|
|
45918
|
+
text = readFileSync24(rc2, "utf-8");
|
|
45213
45919
|
} catch {
|
|
45214
45920
|
continue;
|
|
45215
45921
|
}
|
|
@@ -45219,7 +45925,7 @@ function removeInstallerPathLines() {
|
|
|
45219
45925
|
);
|
|
45220
45926
|
if (kept.length === lines.length) continue;
|
|
45221
45927
|
try {
|
|
45222
|
-
|
|
45928
|
+
writeFileSync16(rc2, kept.join("\n"));
|
|
45223
45929
|
touched.push(rc2);
|
|
45224
45930
|
} catch {
|
|
45225
45931
|
}
|
|
@@ -45227,11 +45933,11 @@ function removeInstallerPathLines() {
|
|
|
45227
45933
|
return touched;
|
|
45228
45934
|
}
|
|
45229
45935
|
function tildify2(p) {
|
|
45230
|
-
const home =
|
|
45936
|
+
const home = homedir17();
|
|
45231
45937
|
return p === home || p.startsWith(home + "/") ? "~" + p.slice(home.length) : p;
|
|
45232
45938
|
}
|
|
45233
45939
|
function resolveCliPath2() {
|
|
45234
|
-
return
|
|
45940
|
+
return resolve18(process.argv[1] ?? "gipity");
|
|
45235
45941
|
}
|
|
45236
45942
|
async function stopDaemon() {
|
|
45237
45943
|
if (!isDaemonRunning()) return;
|
|
@@ -45265,7 +45971,7 @@ function removeServiceUnit() {
|
|
|
45265
45971
|
const r = spawnSyncCommand(argv2[0], argv2.slice(1), { stdio: "ignore" });
|
|
45266
45972
|
if (r.status !== 0) allOk = false;
|
|
45267
45973
|
}
|
|
45268
|
-
if (
|
|
45974
|
+
if (existsSync23(plan2.path)) {
|
|
45269
45975
|
try {
|
|
45270
45976
|
unlinkSync8(plan2.path);
|
|
45271
45977
|
} catch {
|
|
@@ -45288,9 +45994,9 @@ async function revokeDeviceBestEffort() {
|
|
|
45288
45994
|
}
|
|
45289
45995
|
var uninstallCommand = new Command("uninstall").description("Uninstall Gipity").option("--yes", "Skip confirmation prompts").action(async (opts) => {
|
|
45290
45996
|
const autoYes = opts.yes || getAutoConfirm();
|
|
45291
|
-
const gipityDir =
|
|
45292
|
-
const launcherBin =
|
|
45293
|
-
const installedViaLauncher =
|
|
45997
|
+
const gipityDir = join24(homedir17(), ".gipity");
|
|
45998
|
+
const launcherBin = join24(gipityDir, "launcher", "bin", "gipity");
|
|
45999
|
+
const installedViaLauncher = existsSync23(launcherBin);
|
|
45294
46000
|
console.log(`${bold("Gipity uninstall")} - this will:`);
|
|
45295
46001
|
console.log(`\u2022 Stop the running relay daemon (if any)`);
|
|
45296
46002
|
console.log(`\u2022 Remove the OS autostart service (launchd / systemd / Task Scheduler)`);
|
|
@@ -45325,9 +46031,26 @@ var uninstallCommand = new Command("uninstall").description("Uninstall Gipity").
|
|
|
45325
46031
|
} else {
|
|
45326
46032
|
console.log(`${muted("No Gipity entries in Claude Code settings.")}`);
|
|
45327
46033
|
}
|
|
45328
|
-
if (
|
|
46034
|
+
if (grokInstallState().exists) {
|
|
46035
|
+
spawnSyncCommand(resolveCommand("grok"), ["plugin", "uninstall", "gipity", "--confirm"], {
|
|
46036
|
+
stdio: "ignore",
|
|
46037
|
+
timeout: 6e4
|
|
46038
|
+
});
|
|
46039
|
+
console.log(`${success("Gipity plugin removed from Grok.")}`);
|
|
46040
|
+
}
|
|
46041
|
+
const agentSkills = agentSkillsState();
|
|
46042
|
+
if (agentSkills.skills.length) {
|
|
46043
|
+
for (const name of agentSkills.skills) {
|
|
46044
|
+
try {
|
|
46045
|
+
rmSync3(join24(AGENTS_SKILLS_DIR, name), { recursive: true, force: true });
|
|
46046
|
+
} catch {
|
|
46047
|
+
}
|
|
46048
|
+
}
|
|
46049
|
+
console.log(`${success(`Removed ${agentSkills.skills.length} Gipity skills from ~/.agents/skills.`)}`);
|
|
46050
|
+
}
|
|
46051
|
+
if (existsSync23(gipityDir)) {
|
|
45329
46052
|
try {
|
|
45330
|
-
|
|
46053
|
+
rmSync3(gipityDir, { recursive: true, force: true });
|
|
45331
46054
|
console.log(`${success(`Removed ${gipityDir}/`)}`);
|
|
45332
46055
|
} catch (err) {
|
|
45333
46056
|
console.error(`${error(`Could not remove ${gipityDir}: ${err?.message || err}`)}`);
|
|
@@ -45488,7 +46211,7 @@ gmailCommand.command("reply").description("Reply to a Gmail thread. Get thread-i
|
|
|
45488
46211
|
}));
|
|
45489
46212
|
|
|
45490
46213
|
// src/commands/text.ts
|
|
45491
|
-
import { readFileSync as
|
|
46214
|
+
import { readFileSync as readFileSync25 } from "fs";
|
|
45492
46215
|
init_colors();
|
|
45493
46216
|
|
|
45494
46217
|
// src/helpers/text-analysis.ts
|
|
@@ -45643,10 +46366,10 @@ function areAnagrams(a, b7) {
|
|
|
45643
46366
|
|
|
45644
46367
|
// src/commands/text.ts
|
|
45645
46368
|
function resolveInput(args, opts) {
|
|
45646
|
-
if (opts.file) return
|
|
46369
|
+
if (opts.file) return readFileSync25(opts.file, "utf-8");
|
|
45647
46370
|
if (args.length) return args.join(" ");
|
|
45648
46371
|
try {
|
|
45649
|
-
return
|
|
46372
|
+
return readFileSync25(0, "utf-8");
|
|
45650
46373
|
} catch {
|
|
45651
46374
|
return "";
|
|
45652
46375
|
}
|
|
@@ -45851,9 +46574,9 @@ function collectRealFlags(argv2, program3) {
|
|
|
45851
46574
|
}
|
|
45852
46575
|
|
|
45853
46576
|
// src/trace.ts
|
|
45854
|
-
import { openSync as openSync5, writeSync, mkdirSync as
|
|
45855
|
-
import { join as
|
|
45856
|
-
import { homedir as
|
|
46577
|
+
import { openSync as openSync5, writeSync, mkdirSync as mkdirSync17 } from "fs";
|
|
46578
|
+
import { join as join25 } from "path";
|
|
46579
|
+
import { homedir as homedir18 } from "os";
|
|
45857
46580
|
var TRACE_KEY = /* @__PURE__ */ Symbol.for("gipity.traceOutput");
|
|
45858
46581
|
function installOutputTrace(label2) {
|
|
45859
46582
|
if (process.env.GIPITY_TRACE_OUTPUT !== "1") return;
|
|
@@ -45864,10 +46587,10 @@ function installOutputTrace(label2) {
|
|
|
45864
46587
|
existing.emit({ event: "reenter", label: label2 });
|
|
45865
46588
|
return;
|
|
45866
46589
|
}
|
|
45867
|
-
const dir =
|
|
45868
|
-
|
|
46590
|
+
const dir = join25(process.env.GIPITY_DIR || join25(homedir18(), ".gipity"), "trace");
|
|
46591
|
+
mkdirSync17(dir, { recursive: true });
|
|
45869
46592
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", "_").replace(/:/g, "-");
|
|
45870
|
-
const fd2 = openSync5(
|
|
46593
|
+
const fd2 = openSync5(join25(dir, `${stamp}-pid${process.pid}.jsonl`), "a");
|
|
45871
46594
|
const emit = (rec) => {
|
|
45872
46595
|
try {
|
|
45873
46596
|
writeSync(fd2, JSON.stringify({ t: (/* @__PURE__ */ new Date()).toISOString(), ...rec }) + "\n");
|
|
@@ -45903,11 +46626,11 @@ function installOutputTrace(label2) {
|
|
|
45903
46626
|
// src/index.ts
|
|
45904
46627
|
installOutputTrace("index");
|
|
45905
46628
|
var __dirname = dirname13(fileURLToPath3(import.meta.url));
|
|
45906
|
-
var pkg = JSON.parse(
|
|
46629
|
+
var pkg = JSON.parse(readFileSync26(resolve19(__dirname, "../package.json"), "utf-8"));
|
|
45907
46630
|
function versionLabel() {
|
|
45908
46631
|
const base = `v${pkg.version}`;
|
|
45909
46632
|
try {
|
|
45910
|
-
const info2 = JSON.parse(
|
|
46633
|
+
const info2 = JSON.parse(readFileSync26(resolve19(__dirname, "build-info.json"), "utf-8"));
|
|
45911
46634
|
if (info2?.sha) return `${base} (dev ${info2.sha}${info2.dirty ? ", modified" : ""})`;
|
|
45912
46635
|
} catch {
|
|
45913
46636
|
}
|
|
@@ -45949,7 +46672,7 @@ var servicesGroup = [serviceCommand, generateCommand, notifyCommand, paymentsCom
|
|
|
45949
46672
|
var filesGroup = [syncCommand, fileCommand, pushCommand, uploadCommand, storageCommand];
|
|
45950
46673
|
var gipGroup = [chatCommand, memoryCommand, agentCommand, approvalCommand, gmailCommand];
|
|
45951
46674
|
var utilitiesGroup = [sandboxCommand, emailCommand, locationCommand, textCommand, bugCommand];
|
|
45952
|
-
var connectGroup = [loginCommand, logoutCommand,
|
|
46675
|
+
var connectGroup = [loginCommand, logoutCommand, buildCommand, connectCommand, relayCommand, githubCommand, creditsCommand, doctorCommand, updateCommand, uninstallCommand];
|
|
45953
46676
|
var HELP_SECTIONS = [
|
|
45954
46677
|
{ title: "Start here", cmds: startGroup },
|
|
45955
46678
|
{ title: "App build & ship", cmds: buildGroup },
|
|
@@ -46007,9 +46730,9 @@ program2.configureHelp({
|
|
|
46007
46730
|
lines.push("");
|
|
46008
46731
|
lines.push(bold("Quick start:"));
|
|
46009
46732
|
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
|
|
46733
|
+
lines.push(` ${brand("gipity init")} ${dim("- link this dir + wire up every coding agent on this machine")}`);
|
|
46734
|
+
lines.push(` ${brand("gipity connect")} ${dim("- connect this computer to gipity.ai so the web CLI can drive it")}`);
|
|
46735
|
+
lines.push(` ${brand("gipity build")} ${dim("- or start from anywhere: pick a project, pick your agent, go")}`);
|
|
46013
46736
|
lines.push("");
|
|
46014
46737
|
lines.push(bold("Usage:"));
|
|
46015
46738
|
lines.push(` ${cmd.name()} [options] [command]`);
|
|
@@ -46043,6 +46766,8 @@ for (const cmd of HELP_SECTIONS.flatMap((s) => s.cmds)) {
|
|
|
46043
46766
|
`);
|
|
46044
46767
|
program2.addCommand(cmd);
|
|
46045
46768
|
}
|
|
46769
|
+
configureHelp(claudeCommand);
|
|
46770
|
+
program2.addCommand(claudeCommand, { hidden: true });
|
|
46046
46771
|
program2.helpCommand(false);
|
|
46047
46772
|
function manifestCommand(c) {
|
|
46048
46773
|
return {
|