gipity 1.1.4 → 1.1.5
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/adopt-cwd.js +1 -1
- package/dist/banner.js +3 -5
- package/dist/commands/add.js +1 -1
- package/dist/commands/build.js +12 -11
- package/dist/commands/chat.js +1 -1
- package/dist/commands/deploy.js +1 -1
- package/dist/commands/generate.js +5 -5
- package/dist/commands/gmail.js +1 -1
- package/dist/commands/job.js +10 -5
- package/dist/commands/load.js +2 -2
- package/dist/commands/login.js +7 -2
- package/dist/commands/page-eval.js +5 -5
- package/dist/commands/page-fetch.js +14 -10
- package/dist/commands/page-inspect.js +2 -2
- package/dist/commands/page-screenshot.js +5 -5
- package/dist/commands/page-test.js +1 -1
- package/dist/commands/remove.js +1 -1
- package/dist/commands/sandbox.js +1 -1
- package/dist/commands/save.js +1 -1
- package/dist/commands/secrets.js +1 -1
- package/dist/commands/service.js +1 -1
- package/dist/commands/setup.js +4 -6
- package/dist/commands/status.js +41 -12
- package/dist/commands/storage.js +2 -2
- package/dist/commands/sync.js +15 -0
- package/dist/commands/test.js +1 -1
- package/dist/commands/upload.js +1 -1
- package/dist/commands/workflow.js +1 -1
- package/dist/index.js +495 -415
- package/dist/knowledge.js +3 -0
- package/dist/login-flow.js +44 -2
- package/dist/relay/onboarding.js +26 -44
- package/dist/sync.js +6 -6
- package/dist/utils.js +60 -3
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3929,6 +3929,41 @@ var init_colors = __esm({
|
|
|
3929
3929
|
|
|
3930
3930
|
// src/utils.ts
|
|
3931
3931
|
import { createInterface } from "readline";
|
|
3932
|
+
import { readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
3933
|
+
import { basename, join } from "node:path";
|
|
3934
|
+
function isWsl() {
|
|
3935
|
+
if (process.platform !== "linux") return false;
|
|
3936
|
+
if (process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP) return true;
|
|
3937
|
+
try {
|
|
3938
|
+
return /microsoft/i.test(readFileSync("/proc/version", "utf-8"));
|
|
3939
|
+
} catch {
|
|
3940
|
+
return false;
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
function findWindowsTwinProject(projectRoot, usersBase = "/mnt/c/Users") {
|
|
3944
|
+
try {
|
|
3945
|
+
const name = basename(projectRoot);
|
|
3946
|
+
if (!name) return null;
|
|
3947
|
+
const realRoot = realpathSync(projectRoot);
|
|
3948
|
+
for (const user of readdirSync(usersBase)) {
|
|
3949
|
+
if (WINDOWS_PSEUDO_USERS.has(user)) continue;
|
|
3950
|
+
const candidate = join(usersBase, user, "GipityProjects", name);
|
|
3951
|
+
try {
|
|
3952
|
+
if (!statSync(candidate).isDirectory()) continue;
|
|
3953
|
+
if (realpathSync(candidate) === realRoot) continue;
|
|
3954
|
+
return candidate;
|
|
3955
|
+
} catch {
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
} catch {
|
|
3959
|
+
}
|
|
3960
|
+
return null;
|
|
3961
|
+
}
|
|
3962
|
+
function wslPathToWindows(p) {
|
|
3963
|
+
const m = p.match(/^\/mnt\/([a-z])\/(.*)$/);
|
|
3964
|
+
if (!m) return p;
|
|
3965
|
+
return `${m[1].toUpperCase()}:\\${m[2].replace(/\//g, "\\")}`;
|
|
3966
|
+
}
|
|
3932
3967
|
function decodeJwtExp(token) {
|
|
3933
3968
|
try {
|
|
3934
3969
|
const parts = token.split(".");
|
|
@@ -3972,7 +4007,7 @@ async function confirm(question, opts = {}) {
|
|
|
3972
4007
|
return false;
|
|
3973
4008
|
}
|
|
3974
4009
|
const hint = defaultYes ? dim("[Y/n]") : dim("[y/N]");
|
|
3975
|
-
process.stdout.write(`${question} ${hint} `);
|
|
4010
|
+
process.stdout.write(`${question} ${hint}: `);
|
|
3976
4011
|
const { stdin } = process;
|
|
3977
4012
|
const wasRaw = stdin.isRaw ?? false;
|
|
3978
4013
|
stdin.setRawMode(true);
|
|
@@ -4030,11 +4065,12 @@ function formatSize(bytes) {
|
|
|
4030
4065
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
4031
4066
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
4032
4067
|
}
|
|
4033
|
-
var _autoConfirm;
|
|
4068
|
+
var WINDOWS_PSEUDO_USERS, _autoConfirm;
|
|
4034
4069
|
var init_utils = __esm({
|
|
4035
4070
|
"src/utils.ts"() {
|
|
4036
4071
|
"use strict";
|
|
4037
4072
|
init_colors();
|
|
4073
|
+
WINDOWS_PSEUDO_USERS = /* @__PURE__ */ new Set(["All Users", "Default", "Default User", "Public", "desktop.ini"]);
|
|
4038
4074
|
_autoConfirm = false;
|
|
4039
4075
|
}
|
|
4040
4076
|
});
|
|
@@ -4052,14 +4088,14 @@ __export(auth_exports, {
|
|
|
4052
4088
|
saveAuth: () => saveAuth,
|
|
4053
4089
|
sessionExpired: () => sessionExpired
|
|
4054
4090
|
});
|
|
4055
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync, chmodSync, openSync, closeSync } from "fs";
|
|
4056
|
-
import { join } from "path";
|
|
4091
|
+
import { readFileSync as readFileSync2, writeFileSync, mkdirSync, existsSync, unlinkSync, chmodSync, openSync, closeSync } from "fs";
|
|
4092
|
+
import { join as join2 } from "path";
|
|
4057
4093
|
import { homedir } from "os";
|
|
4058
4094
|
function getAuth() {
|
|
4059
4095
|
if (cached) return cached;
|
|
4060
4096
|
if (!existsSync(AUTH_FILE)) return null;
|
|
4061
4097
|
try {
|
|
4062
|
-
cached = JSON.parse(
|
|
4098
|
+
cached = JSON.parse(readFileSync2(AUTH_FILE, "utf-8"));
|
|
4063
4099
|
return cached;
|
|
4064
4100
|
} catch {
|
|
4065
4101
|
return null;
|
|
@@ -4068,7 +4104,7 @@ function getAuth() {
|
|
|
4068
4104
|
function readAuthFresh() {
|
|
4069
4105
|
if (!existsSync(AUTH_FILE)) return null;
|
|
4070
4106
|
try {
|
|
4071
|
-
return JSON.parse(
|
|
4107
|
+
return JSON.parse(readFileSync2(AUTH_FILE, "utf-8"));
|
|
4072
4108
|
} catch {
|
|
4073
4109
|
return null;
|
|
4074
4110
|
}
|
|
@@ -4119,7 +4155,7 @@ async function acquireRefreshLock() {
|
|
|
4119
4155
|
};
|
|
4120
4156
|
} catch {
|
|
4121
4157
|
try {
|
|
4122
|
-
const pid = parseInt(
|
|
4158
|
+
const pid = parseInt(readFileSync2(AUTH_LOCK_FILE, "utf-8").trim(), 10);
|
|
4123
4159
|
if (pid && !isNaN(pid)) {
|
|
4124
4160
|
try {
|
|
4125
4161
|
process.kill(pid, 0);
|
|
@@ -4209,9 +4245,9 @@ var init_auth = __esm({
|
|
|
4209
4245
|
"src/auth.ts"() {
|
|
4210
4246
|
"use strict";
|
|
4211
4247
|
init_utils();
|
|
4212
|
-
AUTH_DIR = process.env.GIPITY_DIR ||
|
|
4213
|
-
AUTH_FILE =
|
|
4214
|
-
AUTH_LOCK_FILE =
|
|
4248
|
+
AUTH_DIR = process.env.GIPITY_DIR || join2(homedir(), ".gipity");
|
|
4249
|
+
AUTH_FILE = join2(AUTH_DIR, "auth.json");
|
|
4250
|
+
AUTH_LOCK_FILE = join2(AUTH_DIR, "auth.lock");
|
|
4215
4251
|
cached = null;
|
|
4216
4252
|
delay = (ms2) => new Promise((r) => setTimeout(r, ms2));
|
|
4217
4253
|
LOCK_WAIT_MS = 1e4;
|
|
@@ -6535,13 +6571,13 @@ var require_tar_stream = __commonJS({
|
|
|
6535
6571
|
});
|
|
6536
6572
|
|
|
6537
6573
|
// src/client-context.ts
|
|
6538
|
-
import { readFileSync as
|
|
6574
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
6539
6575
|
import { resolve, dirname } from "path";
|
|
6540
6576
|
import { fileURLToPath } from "url";
|
|
6541
6577
|
function cliVersion() {
|
|
6542
6578
|
try {
|
|
6543
6579
|
const dir = dirname(fileURLToPath(import.meta.url));
|
|
6544
|
-
const pkg2 = JSON.parse(
|
|
6580
|
+
const pkg2 = JSON.parse(readFileSync3(resolve(dir, "../package.json"), "utf-8"));
|
|
6545
6581
|
return typeof pkg2.version === "string" ? pkg2.version : "unknown";
|
|
6546
6582
|
} catch {
|
|
6547
6583
|
return "unknown";
|
|
@@ -6928,7 +6964,7 @@ __export(config_exports, {
|
|
|
6928
6964
|
setApiBaseOverride: () => setApiBaseOverride,
|
|
6929
6965
|
shouldIgnore: () => shouldIgnore
|
|
6930
6966
|
});
|
|
6931
|
-
import { readFileSync as
|
|
6967
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
|
|
6932
6968
|
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
6933
6969
|
function setApiBaseOverride(url) {
|
|
6934
6970
|
apiBaseOverride = url;
|
|
@@ -6986,7 +7022,7 @@ function getConfig() {
|
|
|
6986
7022
|
const path5 = getConfigPath();
|
|
6987
7023
|
if (!path5) return null;
|
|
6988
7024
|
try {
|
|
6989
|
-
cached3 = JSON.parse(
|
|
7025
|
+
cached3 = JSON.parse(readFileSync4(path5, "utf-8"));
|
|
6990
7026
|
return cached3;
|
|
6991
7027
|
} catch {
|
|
6992
7028
|
return null;
|
|
@@ -31865,7 +31901,7 @@ init_config();
|
|
|
31865
31901
|
init_utils();
|
|
31866
31902
|
import { readFileSync as readFileSync28 } from "fs";
|
|
31867
31903
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
31868
|
-
import { dirname as
|
|
31904
|
+
import { dirname as dirname14, resolve as resolve18 } from "path";
|
|
31869
31905
|
|
|
31870
31906
|
// src/helpers/output.ts
|
|
31871
31907
|
var frameOpen = false;
|
|
@@ -32081,6 +32117,8 @@ Write files locally - Gipity's editor hooks (installed into Claude Code, Grok, a
|
|
|
32081
32117
|
|
|
32082
32118
|
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.
|
|
32083
32119
|
|
|
32120
|
+
**WSL trap: user-dropped files may be in a Windows-side twin.** On WSL, a same-named folder often exists at \`C:\\Users\\<name>\\GipityProjects\\<project>\` (visible as \`/mnt/c/Users/<name>/GipityProjects/<project>\`) - from Windows Explorer it looks like "the project folder", so users drop new files (audio, images) there. Nothing syncs from it. If the user says files are "in the project" but you can't find them, check that path before searching wider; \`gipity sync\` prints a notice when such a twin exists.
|
|
32121
|
+
|
|
32084
32122
|
### Where files go: deploy only ships \`src/\`
|
|
32085
32123
|
|
|
32086
32124
|
Deploy is opt-in, not opt-out: the \`files\` phase uploads **only** what's under \`src/\` (plus \`functions/\` and \`migrations/\` as backend, not CDN files). Anything else at the project root is kept but never deployed. Put each kind of file in the right bucket so scratch and reference material can't bloat a deploy:
|
|
@@ -32117,6 +32155,7 @@ App development skills:
|
|
|
32117
32155
|
- \`app-debugging\` - debug a deployed app: page inspect/eval, screenshots, function logs
|
|
32118
32156
|
- \`app-development\` - functions, database, and API
|
|
32119
32157
|
- \`app-import\` - import apps from GitHub/.gip bundles (incl. Vercel/Replit/Lovable porting) and export any project as a portable .gip - app_import tool, gipity save/load
|
|
32158
|
+
- \`app-records\` - Gipity Records: declared tables \u2192 validated CRUD, provenance, workflows, auto UI
|
|
32120
32159
|
- \`app-testing\` - testing deployed app functions (ctx.fn.call/callAs, the isolated test DB)
|
|
32121
32160
|
- \`deploy\` - the deploy pipeline & gipity.yaml manifest
|
|
32122
32161
|
- \`jobs\` - long-running CPU + GPU compute jobs (Python / Node / bash)
|
|
@@ -32153,13 +32192,13 @@ init_colors();
|
|
|
32153
32192
|
|
|
32154
32193
|
// src/bug-queue.ts
|
|
32155
32194
|
init_api();
|
|
32156
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, readFileSync as
|
|
32157
|
-
import { join as
|
|
32195
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
32196
|
+
import { join as join3 } from "path";
|
|
32158
32197
|
import { homedir as homedir2 } from "os";
|
|
32159
|
-
var QUEUE_DIR =
|
|
32198
|
+
var QUEUE_DIR = join3(process.env.GIPITY_DIR || join3(homedir2(), ".gipity"), "bug-queue");
|
|
32160
32199
|
function queueBugReport(report) {
|
|
32161
32200
|
mkdirSync2(QUEUE_DIR, { recursive: true, mode: 448 });
|
|
32162
|
-
const file =
|
|
32201
|
+
const file = join3(QUEUE_DIR, `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`);
|
|
32163
32202
|
writeFileSync3(file, JSON.stringify(report, null, 2), { mode: 384 });
|
|
32164
32203
|
}
|
|
32165
32204
|
function isRetryableFailure(err) {
|
|
@@ -32171,12 +32210,12 @@ function isRetryableFailure(err) {
|
|
|
32171
32210
|
async function flushBugQueue() {
|
|
32172
32211
|
if (!existsSync3(QUEUE_DIR)) return 0;
|
|
32173
32212
|
let delivered = 0;
|
|
32174
|
-
for (const file of
|
|
32213
|
+
for (const file of readdirSync2(QUEUE_DIR)) {
|
|
32175
32214
|
if (!file.endsWith(".json")) continue;
|
|
32176
|
-
const path5 =
|
|
32215
|
+
const path5 = join3(QUEUE_DIR, file);
|
|
32177
32216
|
let report;
|
|
32178
32217
|
try {
|
|
32179
|
-
report = JSON.parse(
|
|
32218
|
+
report = JSON.parse(readFileSync5(path5, "utf-8"));
|
|
32180
32219
|
} catch {
|
|
32181
32220
|
try {
|
|
32182
32221
|
unlinkSync2(path5);
|
|
@@ -32199,6 +32238,70 @@ async function flushBugQueue() {
|
|
|
32199
32238
|
return delivered;
|
|
32200
32239
|
}
|
|
32201
32240
|
|
|
32241
|
+
// src/login-flow.ts
|
|
32242
|
+
init_api();
|
|
32243
|
+
init_auth();
|
|
32244
|
+
init_config();
|
|
32245
|
+
init_utils();
|
|
32246
|
+
init_colors();
|
|
32247
|
+
function newAccountWouldBeUnexpected(email, priorAuth) {
|
|
32248
|
+
return !!getConfig() || priorAuth?.email.toLowerCase() === email.toLowerCase().trim();
|
|
32249
|
+
}
|
|
32250
|
+
function warnBeforeCodeIfUnexpectedNewAccount(isNewUser, email, indent = "") {
|
|
32251
|
+
if (isNewUser !== true) return;
|
|
32252
|
+
const priorAuth = getAuth();
|
|
32253
|
+
if (!newAccountWouldBeUnexpected(email, priorAuth)) return;
|
|
32254
|
+
const config = getConfig();
|
|
32255
|
+
console.log(`${indent}${warning(`No existing Gipity account for ${email} \u2014 entering the code will CREATE a new one.`)}`);
|
|
32256
|
+
if (config) {
|
|
32257
|
+
console.log(`${indent}${muted(`This directory is linked to project ${config.projectSlug} (account ${config.accountSlug}). If you meant to log into that account, stop and re-check the email before entering the code.`)}`);
|
|
32258
|
+
}
|
|
32259
|
+
}
|
|
32260
|
+
function warnIfUnexpectedNewAccount(isNewUser, email, priorAuth, indent = "") {
|
|
32261
|
+
if (isNewUser !== true) return;
|
|
32262
|
+
if (!newAccountWouldBeUnexpected(email, priorAuth)) return;
|
|
32263
|
+
const config = getConfig();
|
|
32264
|
+
console.log(`${indent}${warning(`Logged into a NEW, empty account for ${email} \u2014 no prior account existed for this email.`)}`);
|
|
32265
|
+
if (config) {
|
|
32266
|
+
console.log(`${indent}${muted(`This directory is linked to project ${config.projectSlug} (account ${config.accountSlug}), which the new account does not own \u2014 project / skill / sync commands will fail with "not found".`)}`);
|
|
32267
|
+
}
|
|
32268
|
+
console.log(`${indent}${muted("If you expected an existing account, run: gipity status \u2014 then gipity login again with the correct email.")}`);
|
|
32269
|
+
}
|
|
32270
|
+
async function interactiveLogin() {
|
|
32271
|
+
const email = await prompt(" Email: ");
|
|
32272
|
+
if (!email) {
|
|
32273
|
+
console.error(`
|
|
32274
|
+
${error("Email required.")}`);
|
|
32275
|
+
process.exit(1);
|
|
32276
|
+
}
|
|
32277
|
+
const sendRes = await publicPost("/auth/login", { email });
|
|
32278
|
+
console.log(" Check your email for a 6-digit code.\n");
|
|
32279
|
+
warnBeforeCodeIfUnexpectedNewAccount(sendRes.isNewUser, email, " ");
|
|
32280
|
+
const code = await prompt(" Code: ");
|
|
32281
|
+
if (!code) {
|
|
32282
|
+
console.error(`
|
|
32283
|
+
${error("Code required.")}`);
|
|
32284
|
+
process.exit(1);
|
|
32285
|
+
}
|
|
32286
|
+
const priorAuth = getAuth();
|
|
32287
|
+
const res = await publicPost("/auth/verify", { email, code });
|
|
32288
|
+
const exp = decodeJwtExp(res.accessToken);
|
|
32289
|
+
if (!exp) {
|
|
32290
|
+
console.error(`
|
|
32291
|
+
${error("Invalid token received.")}`);
|
|
32292
|
+
process.exit(1);
|
|
32293
|
+
}
|
|
32294
|
+
const expiresAt = new Date(exp * 1e3).toISOString();
|
|
32295
|
+
saveAuth({ accessToken: res.accessToken, refreshToken: res.refreshToken, email, expiresAt });
|
|
32296
|
+
console.log(` ${success(`Logged in (${email}).`)}`);
|
|
32297
|
+
warnIfUnexpectedNewAccount(res.isNewUser, email, priorAuth, " ");
|
|
32298
|
+
const delivered = await flushBugQueue().catch(() => 0);
|
|
32299
|
+
if (delivered > 0) {
|
|
32300
|
+
console.log(` ${muted(`Delivered ${delivered} queued bug report${delivered === 1 ? "" : "s"}.`)}`);
|
|
32301
|
+
}
|
|
32302
|
+
return getAuth();
|
|
32303
|
+
}
|
|
32304
|
+
|
|
32202
32305
|
// src/commands/login.ts
|
|
32203
32306
|
var loginCommand = new Command("login").description("Log in or sign up").option("--email <email>", "Email address").option("--code <code>", "Verification code").action(async (opts) => {
|
|
32204
32307
|
try {
|
|
@@ -32209,9 +32312,10 @@ var loginCommand = new Command("login").description("Log in or sign up").option(
|
|
|
32209
32312
|
return;
|
|
32210
32313
|
}
|
|
32211
32314
|
if (email && !code) {
|
|
32212
|
-
await publicPost("/auth/login", { email });
|
|
32315
|
+
const sendRes2 = await publicPost("/auth/login", { email });
|
|
32213
32316
|
console.log("Check your email for a 6-digit code.");
|
|
32214
32317
|
console.log(muted(`Then run: gipity login --email ${email} --code <code>`));
|
|
32318
|
+
warnBeforeCodeIfUnexpectedNewAccount(sendRes2.isNewUser, email);
|
|
32215
32319
|
return;
|
|
32216
32320
|
}
|
|
32217
32321
|
console.log("Enter your email to log in or create an account.");
|
|
@@ -32222,9 +32326,10 @@ var loginCommand = new Command("login").description("Log in or sign up").option(
|
|
|
32222
32326
|
console.error(error("Email required."));
|
|
32223
32327
|
process.exit(1);
|
|
32224
32328
|
}
|
|
32225
|
-
await publicPost("/auth/login", { email });
|
|
32329
|
+
const sendRes = await publicPost("/auth/login", { email });
|
|
32226
32330
|
console.log("");
|
|
32227
32331
|
console.log("Check your email for a 6-digit code.");
|
|
32332
|
+
warnBeforeCodeIfUnexpectedNewAccount(sendRes.isNewUser, email);
|
|
32228
32333
|
code = await prompt("Code: ");
|
|
32229
32334
|
await verify(email, code);
|
|
32230
32335
|
} catch (err) {
|
|
@@ -32233,6 +32338,7 @@ var loginCommand = new Command("login").description("Log in or sign up").option(
|
|
|
32233
32338
|
}
|
|
32234
32339
|
});
|
|
32235
32340
|
async function verify(email, code) {
|
|
32341
|
+
const priorAuth = getAuth();
|
|
32236
32342
|
const res = await publicPost("/auth/verify", { email, code });
|
|
32237
32343
|
const exp = decodeJwtExp(res.accessToken);
|
|
32238
32344
|
if (!exp) {
|
|
@@ -32247,6 +32353,7 @@ async function verify(email, code) {
|
|
|
32247
32353
|
expiresAt
|
|
32248
32354
|
});
|
|
32249
32355
|
console.log(success(`Logged in (${email}).`));
|
|
32356
|
+
warnIfUnexpectedNewAccount(res.isNewUser, email, priorAuth);
|
|
32250
32357
|
const delivered = await flushBugQueue().catch(() => 0);
|
|
32251
32358
|
if (delivered > 0) {
|
|
32252
32359
|
console.log(muted(`Delivered ${delivered} queued bug report${delivered === 1 ? "" : "s"}.`));
|
|
@@ -32292,8 +32399,8 @@ function run(label2, action) {
|
|
|
32292
32399
|
init_api();
|
|
32293
32400
|
init_config();
|
|
32294
32401
|
init_utils();
|
|
32295
|
-
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync5, statSync, lstatSync, unlinkSync as unlinkSync3, readdirSync as
|
|
32296
|
-
import { join as
|
|
32402
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync5, statSync as statSync2, lstatSync, unlinkSync as unlinkSync3, readdirSync as readdirSync4, rmdirSync, readFileSync as readFileSync7, renameSync, openSync as openSync2, closeSync as closeSync2, utimesSync, realpathSync as realpathSync2 } from "fs";
|
|
32403
|
+
import { join as join5, relative, dirname as dirname4, extname as extname2, resolve as resolve4, sep } from "path";
|
|
32297
32404
|
import { hostname } from "os";
|
|
32298
32405
|
import { createHash as createHash2 } from "crypto";
|
|
32299
32406
|
|
|
@@ -32407,9 +32514,9 @@ var UploadConflictError = class extends Error {
|
|
|
32407
32514
|
path;
|
|
32408
32515
|
};
|
|
32409
32516
|
async function uploadOneFile(projectGuid, localPath, virtualPath, opts = {}) {
|
|
32410
|
-
const { sha256
|
|
32517
|
+
const { sha256, size } = await hashFile(localPath);
|
|
32411
32518
|
const mime = opts.mime ?? guessMime(virtualPath);
|
|
32412
|
-
const initBody = { path: virtualPath, size, sha256
|
|
32519
|
+
const initBody = { path: virtualPath, size, sha256, mime };
|
|
32413
32520
|
if (opts.expectedServerVersion !== void 0) {
|
|
32414
32521
|
initBody.expected_server_version = opts.expectedServerVersion;
|
|
32415
32522
|
}
|
|
@@ -32537,9 +32644,9 @@ async function uploadCompleteBatch(projectGuid, items) {
|
|
|
32537
32644
|
|
|
32538
32645
|
// src/setup.ts
|
|
32539
32646
|
init_platform();
|
|
32540
|
-
import { resolve as resolve3, join as
|
|
32647
|
+
import { resolve as resolve3, join as join4, dirname as dirname3 } from "path";
|
|
32541
32648
|
import { homedir as homedir3, tmpdir } from "os";
|
|
32542
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readFileSync as
|
|
32649
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readFileSync as readFileSync6, mkdtempSync, rmSync, cpSync, readdirSync as readdirSync3 } from "fs";
|
|
32543
32650
|
init_config();
|
|
32544
32651
|
var PRIMER_FILES = {
|
|
32545
32652
|
claude: "CLAUDE.md",
|
|
@@ -32654,14 +32761,14 @@ function stripGipityHooks(settings) {
|
|
|
32654
32761
|
function readSettingsFile(path5) {
|
|
32655
32762
|
if (!existsSync4(path5)) return {};
|
|
32656
32763
|
try {
|
|
32657
|
-
return JSON.parse(
|
|
32764
|
+
return JSON.parse(readFileSync6(path5, "utf-8"));
|
|
32658
32765
|
} catch {
|
|
32659
32766
|
return {};
|
|
32660
32767
|
}
|
|
32661
32768
|
}
|
|
32662
32769
|
function ensureGipityPlugin(force = false) {
|
|
32663
|
-
const claudeDir =
|
|
32664
|
-
const settingsPath =
|
|
32770
|
+
const claudeDir = join4(homedir3(), ".claude");
|
|
32771
|
+
const settingsPath = join4(claudeDir, "settings.json");
|
|
32665
32772
|
const settings = readSettingsFile(settingsPath);
|
|
32666
32773
|
let changed = stripGipityHooks(settings);
|
|
32667
32774
|
const marketplaces = settings.extraKnownMarketplaces ?? (settings.extraKnownMarketplaces = {});
|
|
@@ -32693,8 +32800,8 @@ function versionGte(have, want) {
|
|
|
32693
32800
|
}
|
|
32694
32801
|
function userScopeInstallState() {
|
|
32695
32802
|
try {
|
|
32696
|
-
const p =
|
|
32697
|
-
const data = JSON.parse(
|
|
32803
|
+
const p = join4(homedir3(), ".claude", "plugins", "installed_plugins.json");
|
|
32804
|
+
const data = JSON.parse(readFileSync6(p, "utf-8"));
|
|
32698
32805
|
const entries = data?.plugins?.[GIPITY_PLUGIN_ID];
|
|
32699
32806
|
if (!Array.isArray(entries)) return { exists: false, current: false };
|
|
32700
32807
|
const userEntries = entries.filter((e) => e?.scope === "user");
|
|
@@ -32731,8 +32838,8 @@ function ensureGipityPluginInstalled() {
|
|
|
32731
32838
|
}
|
|
32732
32839
|
function grokInstallState() {
|
|
32733
32840
|
try {
|
|
32734
|
-
const p =
|
|
32735
|
-
const data = JSON.parse(
|
|
32841
|
+
const p = join4(homedir3(), ".grok", "installed-plugins", "registry.json");
|
|
32842
|
+
const data = JSON.parse(readFileSync6(p, "utf-8"));
|
|
32736
32843
|
const versions = [];
|
|
32737
32844
|
for (const repo of Object.values(data?.repos ?? {})) {
|
|
32738
32845
|
const v7 = repo?.plugins?.gipity?.version;
|
|
@@ -32757,12 +32864,12 @@ function ensureGrokPluginInstalled() {
|
|
|
32757
32864
|
console.log("Installed the Gipity plugin for Grok (skills + file-sync hooks).");
|
|
32758
32865
|
}
|
|
32759
32866
|
}
|
|
32760
|
-
var AGENTS_SKILLS_DIR =
|
|
32761
|
-
var AGENT_HOOKS_DIR =
|
|
32762
|
-
var AGENT_SKILLS_MANIFEST =
|
|
32867
|
+
var AGENTS_SKILLS_DIR = join4(homedir3(), ".agents", "skills");
|
|
32868
|
+
var AGENT_HOOKS_DIR = join4(homedir3(), ".gipity", "agent-hooks");
|
|
32869
|
+
var AGENT_SKILLS_MANIFEST = join4(homedir3(), ".gipity", "agent-skills.json");
|
|
32763
32870
|
function agentSkillsState() {
|
|
32764
32871
|
try {
|
|
32765
|
-
const m = JSON.parse(
|
|
32872
|
+
const m = JSON.parse(readFileSync6(AGENT_SKILLS_MANIFEST, "utf-8"));
|
|
32766
32873
|
return {
|
|
32767
32874
|
current: typeof m?.version === "string" && versionGte(m.version, GIPITY_PLUGIN_VERSION),
|
|
32768
32875
|
skills: Array.isArray(m?.skills) ? m.skills : []
|
|
@@ -32774,36 +32881,36 @@ function agentSkillsState() {
|
|
|
32774
32881
|
function ensureAgentSkillsInstalled() {
|
|
32775
32882
|
if (agentSkillsState().current) return;
|
|
32776
32883
|
if (!binaryOnPath("git")) return;
|
|
32777
|
-
const tmp = mkdtempSync(
|
|
32884
|
+
const tmp = mkdtempSync(join4(tmpdir(), "gipity-skills-"));
|
|
32778
32885
|
try {
|
|
32779
32886
|
const clone = spawnSyncCommand(
|
|
32780
32887
|
resolveCommand("git"),
|
|
32781
|
-
["clone", "--depth", "1", `https://github.com/${GIPITY_MARKETPLACE_REPO}.git`,
|
|
32888
|
+
["clone", "--depth", "1", `https://github.com/${GIPITY_MARKETPLACE_REPO}.git`, join4(tmp, "repo")],
|
|
32782
32889
|
{ stdio: "ignore", timeout: 12e4 }
|
|
32783
32890
|
);
|
|
32784
32891
|
if (clone.status !== 0) return;
|
|
32785
|
-
const repo =
|
|
32892
|
+
const repo = join4(tmp, "repo");
|
|
32786
32893
|
let version = GIPITY_PLUGIN_VERSION;
|
|
32787
32894
|
try {
|
|
32788
|
-
const manifest = JSON.parse(
|
|
32895
|
+
const manifest = JSON.parse(readFileSync6(join4(repo, ".claude-plugin", "plugin.json"), "utf-8"));
|
|
32789
32896
|
if (typeof manifest?.version === "string") version = manifest.version;
|
|
32790
32897
|
} catch {
|
|
32791
32898
|
}
|
|
32792
|
-
const skillsSrc =
|
|
32899
|
+
const skillsSrc = join4(repo, "skills");
|
|
32793
32900
|
const names = [];
|
|
32794
|
-
for (const entry of
|
|
32901
|
+
for (const entry of readdirSync3(skillsSrc, { withFileTypes: true })) {
|
|
32795
32902
|
if (!entry.isDirectory()) continue;
|
|
32796
|
-
if (!existsSync4(
|
|
32903
|
+
if (!existsSync4(join4(skillsSrc, entry.name, "SKILL.md"))) continue;
|
|
32797
32904
|
mkdirSync3(AGENTS_SKILLS_DIR, { recursive: true });
|
|
32798
|
-
cpSync(
|
|
32905
|
+
cpSync(join4(skillsSrc, entry.name), join4(AGENTS_SKILLS_DIR, entry.name), {
|
|
32799
32906
|
recursive: true,
|
|
32800
32907
|
force: true
|
|
32801
32908
|
});
|
|
32802
32909
|
names.push(entry.name);
|
|
32803
32910
|
}
|
|
32804
32911
|
mkdirSync3(AGENT_HOOKS_DIR, { recursive: true });
|
|
32805
|
-
for (const script of
|
|
32806
|
-
cpSync(
|
|
32912
|
+
for (const script of readdirSync3(join4(repo, "hooks", "scripts"))) {
|
|
32913
|
+
cpSync(join4(repo, "hooks", "scripts", script), join4(AGENT_HOOKS_DIR, script), { force: true });
|
|
32807
32914
|
}
|
|
32808
32915
|
writeFileSync4(AGENT_SKILLS_MANIFEST, JSON.stringify({ version, skills: names }, null, 2) + "\n");
|
|
32809
32916
|
console.log(`Installed ${names.length} Gipity skills for Codex (~/.agents/skills).`);
|
|
@@ -32813,8 +32920,8 @@ function ensureAgentSkillsInstalled() {
|
|
|
32813
32920
|
}
|
|
32814
32921
|
}
|
|
32815
32922
|
function applyCodexHooks(existing) {
|
|
32816
|
-
const launcher =
|
|
32817
|
-
const cmd = (script, ...args) => [`sh "${launcher}" "${
|
|
32923
|
+
const launcher = join4(AGENT_HOOKS_DIR, "launch.sh");
|
|
32924
|
+
const cmd = (script, ...args) => [`sh "${launcher}" "${join4(AGENT_HOOKS_DIR, script)}"`, ...args].join(" ");
|
|
32818
32925
|
const wanted = [
|
|
32819
32926
|
{ event: "PostToolUse", matcher: "Edit|Write", command: cmd("sync-push.cjs"), timeout: 30 },
|
|
32820
32927
|
{ event: "UserPromptSubmit", command: cmd("sync-pull.cjs"), timeout: 300 },
|
|
@@ -32852,8 +32959,8 @@ function setupCodexHooks() {
|
|
|
32852
32959
|
if (process.platform === "win32") return;
|
|
32853
32960
|
const cwd = resolve3(process.cwd());
|
|
32854
32961
|
if (cwd === resolve3(homedir3())) return;
|
|
32855
|
-
const path5 =
|
|
32856
|
-
const existing = existsSync4(path5) ?
|
|
32962
|
+
const path5 = join4(cwd, ".codex", "hooks.json");
|
|
32963
|
+
const existing = existsSync4(path5) ? readFileSync6(path5, "utf-8") : null;
|
|
32857
32964
|
const next = applyCodexHooks(existing);
|
|
32858
32965
|
if (next === null) return;
|
|
32859
32966
|
mkdirSync3(dirname3(path5), { recursive: true });
|
|
@@ -32873,9 +32980,9 @@ function setupClaudeHooks() {
|
|
|
32873
32980
|
ensureGipityPlugin();
|
|
32874
32981
|
const cwd = resolve3(process.cwd());
|
|
32875
32982
|
if (cwd === resolve3(homedir3())) return;
|
|
32876
|
-
const claudeDir =
|
|
32983
|
+
const claudeDir = join4(cwd, ".claude");
|
|
32877
32984
|
mkdirSync3(claudeDir, { recursive: true });
|
|
32878
|
-
const settingsPath =
|
|
32985
|
+
const settingsPath = join4(claudeDir, "settings.json");
|
|
32879
32986
|
const settings = readSettingsFile(settingsPath);
|
|
32880
32987
|
stripGipityHooks(settings);
|
|
32881
32988
|
const perms = settings.permissions || {};
|
|
@@ -32922,7 +33029,7 @@ function applySkillsBlock(existing, apiBase2 = DEFAULT_API_BASE) {
|
|
|
32922
33029
|
function writeSkillsFile(relPath, wrap) {
|
|
32923
33030
|
const path5 = resolve3(process.cwd(), relPath);
|
|
32924
33031
|
mkdirSync3(dirname3(path5), { recursive: true });
|
|
32925
|
-
const existing = existsSync4(path5) ?
|
|
33032
|
+
const existing = existsSync4(path5) ? readFileSync6(path5, "utf-8") : null;
|
|
32926
33033
|
const baseNext = applySkillsBlock(existing, resolveApiBase());
|
|
32927
33034
|
const next = wrap && existing === null ? wrap(baseNext) : baseNext;
|
|
32928
33035
|
if (next !== existing) writeFileSync4(path5, next);
|
|
@@ -32965,7 +33072,7 @@ function applyAiderConf(existing) {
|
|
|
32965
33072
|
function setupAiderMd() {
|
|
32966
33073
|
writeSkillsFile(PRIMER_FILES.aider);
|
|
32967
33074
|
const path5 = resolve3(process.cwd(), AIDER_CONF_FILE);
|
|
32968
|
-
const existing = existsSync4(path5) ?
|
|
33075
|
+
const existing = existsSync4(path5) ? readFileSync6(path5, "utf-8") : null;
|
|
32969
33076
|
const next = applyAiderConf(existing);
|
|
32970
33077
|
if (next !== null) writeFileSync4(path5, next);
|
|
32971
33078
|
}
|
|
@@ -33007,7 +33114,7 @@ function setupGitignore() {
|
|
|
33007
33114
|
const gitignorePath = resolve3(process.cwd(), ".gitignore");
|
|
33008
33115
|
const entries = [".gipity/", ".gipity.json", ...SCRATCH_IGNORE];
|
|
33009
33116
|
if (existsSync4(gitignorePath)) {
|
|
33010
|
-
let content =
|
|
33117
|
+
let content = readFileSync6(gitignorePath, "utf-8");
|
|
33011
33118
|
const lines = content.split(/\r?\n/);
|
|
33012
33119
|
const toAdd = entries.filter((e) => !lines.includes(e));
|
|
33013
33120
|
if (toAdd.length > 0) {
|
|
@@ -33035,11 +33142,11 @@ var BULK_DELETE_FRACTION = 0.25;
|
|
|
33035
33142
|
var DOWNLOAD_IDLE_MS = 3e4;
|
|
33036
33143
|
function syncStatePath() {
|
|
33037
33144
|
const configPath = getConfigPath();
|
|
33038
|
-
return
|
|
33145
|
+
return join5(dirname4(configPath), ".gipity", "sync-state.json");
|
|
33039
33146
|
}
|
|
33040
33147
|
function lockPath() {
|
|
33041
33148
|
const configPath = getConfigPath();
|
|
33042
|
-
return
|
|
33149
|
+
return join5(dirname4(configPath), ".gipity", "sync.lock");
|
|
33043
33150
|
}
|
|
33044
33151
|
function projectDir() {
|
|
33045
33152
|
const configPath = getConfigPath();
|
|
@@ -33053,8 +33160,8 @@ function isLockReclaimable(path5, now = Date.now()) {
|
|
|
33053
33160
|
let raw;
|
|
33054
33161
|
let mtimeMs;
|
|
33055
33162
|
try {
|
|
33056
|
-
raw =
|
|
33057
|
-
mtimeMs =
|
|
33163
|
+
raw = readFileSync7(path5, "utf-8").trim();
|
|
33164
|
+
mtimeMs = statSync2(path5).mtimeMs;
|
|
33058
33165
|
} catch {
|
|
33059
33166
|
return false;
|
|
33060
33167
|
}
|
|
@@ -33113,7 +33220,7 @@ async function acquireLock(progress) {
|
|
|
33113
33220
|
);
|
|
33114
33221
|
}
|
|
33115
33222
|
if (!waitSpinner) {
|
|
33116
|
-
waitSpinner = progress?.spinner("Waiting for another sync to finish
|
|
33223
|
+
waitSpinner = progress?.spinner("Waiting for another sync to finish...") ?? null;
|
|
33117
33224
|
}
|
|
33118
33225
|
await new Promise((r) => setTimeout(r, LOCK_POLL_MS2));
|
|
33119
33226
|
}
|
|
@@ -33123,7 +33230,7 @@ function readBaseline(projectGuid) {
|
|
|
33123
33230
|
const path5 = syncStatePath();
|
|
33124
33231
|
if (!existsSync5(path5)) return { projectGuid, files: {}, lastFullSync: null };
|
|
33125
33232
|
try {
|
|
33126
|
-
const parsed = JSON.parse(
|
|
33233
|
+
const parsed = JSON.parse(readFileSync7(path5, "utf-8"));
|
|
33127
33234
|
if (parsed.projectGuid !== projectGuid) {
|
|
33128
33235
|
return { projectGuid, files: {}, lastFullSync: null };
|
|
33129
33236
|
}
|
|
@@ -33147,25 +33254,25 @@ function walkLocal(root, ignorePatterns, baseline) {
|
|
|
33147
33254
|
function walk(dir) {
|
|
33148
33255
|
let entries;
|
|
33149
33256
|
try {
|
|
33150
|
-
entries =
|
|
33257
|
+
entries = readdirSync4(dir, { withFileTypes: true });
|
|
33151
33258
|
} catch {
|
|
33152
33259
|
return;
|
|
33153
33260
|
}
|
|
33154
33261
|
for (const entry of entries) {
|
|
33155
|
-
const full =
|
|
33262
|
+
const full = join5(dir, entry.name);
|
|
33156
33263
|
const rel2 = relative(root, full).replace(/\\/g, "/");
|
|
33157
33264
|
if (shouldIgnore(rel2, ignorePatterns)) continue;
|
|
33158
33265
|
if (entry.isDirectory()) {
|
|
33159
|
-
if (existsSync5(
|
|
33266
|
+
if (existsSync5(join5(full, CONFIG_FILE2))) continue;
|
|
33160
33267
|
walk(full);
|
|
33161
33268
|
} else if (entry.isFile()) {
|
|
33162
33269
|
try {
|
|
33163
|
-
const stat2 =
|
|
33270
|
+
const stat2 = statSync2(full);
|
|
33164
33271
|
const size = stat2.size;
|
|
33165
33272
|
const mtime = stat2.mtime.toISOString();
|
|
33166
33273
|
const prior = baseline[rel2];
|
|
33167
|
-
const
|
|
33168
|
-
result.set(rel2, { size, mtime, sha256
|
|
33274
|
+
const sha256 = prior && prior.size === size && prior.mtime === mtime ? prior.sha256 : void 0;
|
|
33275
|
+
result.set(rel2, { size, mtime, sha256 });
|
|
33169
33276
|
} catch {
|
|
33170
33277
|
}
|
|
33171
33278
|
}
|
|
@@ -33179,8 +33286,8 @@ async function ensureLocalHashes(root, local, paths) {
|
|
|
33179
33286
|
const info2 = local.get(path5);
|
|
33180
33287
|
if (!info2 || info2.sha256) continue;
|
|
33181
33288
|
try {
|
|
33182
|
-
const { sha256
|
|
33183
|
-
info2.sha256 =
|
|
33289
|
+
const { sha256 } = await hashFile(join5(root, path5));
|
|
33290
|
+
info2.sha256 = sha256;
|
|
33184
33291
|
} catch {
|
|
33185
33292
|
}
|
|
33186
33293
|
}
|
|
@@ -33195,10 +33302,10 @@ function resolveInRoot(root, relPath) {
|
|
|
33195
33302
|
throw new Error(`Refusing path outside project root: ${relPath}`);
|
|
33196
33303
|
}
|
|
33197
33304
|
try {
|
|
33198
|
-
const rootReal =
|
|
33305
|
+
const rootReal = realpathSync2(rootResolved);
|
|
33199
33306
|
let ancestor = full;
|
|
33200
33307
|
while (!existsSync5(ancestor) && dirname4(ancestor) !== ancestor) ancestor = dirname4(ancestor);
|
|
33201
|
-
const ancestorReal =
|
|
33308
|
+
const ancestorReal = realpathSync2(ancestor);
|
|
33202
33309
|
if (ancestorReal !== rootReal && !ancestorReal.startsWith(rootReal + sep)) {
|
|
33203
33310
|
throw new Error(`Refusing path outside project root (symlink escape): ${relPath}`);
|
|
33204
33311
|
}
|
|
@@ -33486,9 +33593,9 @@ Plan deletes ${totalDeletes} files (${Math.round(fraction * 100)}% of the tree).
|
|
|
33486
33593
|
}
|
|
33487
33594
|
var GIPITY_IGNORE_FILE = ".gipityignore";
|
|
33488
33595
|
function readGipityIgnore(root) {
|
|
33489
|
-
const path5 =
|
|
33596
|
+
const path5 = join5(root, GIPITY_IGNORE_FILE);
|
|
33490
33597
|
if (!existsSync5(path5)) return [];
|
|
33491
|
-
return
|
|
33598
|
+
return readFileSync7(path5, "utf8").split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\.\//, "").replace(/^\//, ""));
|
|
33492
33599
|
}
|
|
33493
33600
|
function effectiveIgnore(root, configIgnore) {
|
|
33494
33601
|
const base = configIgnore && configIgnore.length ? configIgnore : DEFAULT_SYNC_IGNORE;
|
|
@@ -33529,9 +33636,9 @@ async function syncInner(projectGuid, root, ignore2, opts, interactive) {
|
|
|
33529
33636
|
const config = { projectGuid, ignore: ignore2 };
|
|
33530
33637
|
const p = opts.progress;
|
|
33531
33638
|
const baseline = readBaseline(projectGuid);
|
|
33532
|
-
p?.phase("Scanning local files
|
|
33639
|
+
p?.phase("Scanning local files...");
|
|
33533
33640
|
const local = walkLocal(root, ignore2, baseline.files);
|
|
33534
|
-
p?.phase("Checking Gipity for changes
|
|
33641
|
+
p?.phase("Checking Gipity for changes...");
|
|
33535
33642
|
const remote = await fetchRemote(projectGuid);
|
|
33536
33643
|
for (const path5 of [...remote.keys()]) {
|
|
33537
33644
|
if (shouldIgnore(path5, ignore2)) remote.delete(path5);
|
|
@@ -33542,7 +33649,7 @@ async function syncInner(projectGuid, root, ignore2, opts, interactive) {
|
|
|
33542
33649
|
const r = remote.get(path5);
|
|
33543
33650
|
if (r?.sha256 || baseline.files[path5]) needHash.push(path5);
|
|
33544
33651
|
}
|
|
33545
|
-
if (needHash.length) p?.phase(`Hashing ${needHash.length} file${needHash.length === 1 ? "" : "s"}
|
|
33652
|
+
if (needHash.length) p?.phase(`Hashing ${needHash.length} file${needHash.length === 1 ? "" : "s"}...`);
|
|
33546
33653
|
await ensureLocalHashes(root, local, needHash);
|
|
33547
33654
|
const planned = plan(local, remote, baseline.files);
|
|
33548
33655
|
if (opts.plan) {
|
|
@@ -33628,13 +33735,13 @@ async function syncInner(projectGuid, root, ignore2, opts, interactive) {
|
|
|
33628
33735
|
if (buf) downloadedBytes.set(a.path, buf);
|
|
33629
33736
|
}
|
|
33630
33737
|
} catch (err) {
|
|
33631
|
-
errors.push(`Bulk download incomplete (${err.message}); recovering files individually
|
|
33738
|
+
errors.push(`Bulk download incomplete (${err.message}); recovering files individually...`);
|
|
33632
33739
|
} finally {
|
|
33633
33740
|
p?.finish();
|
|
33634
33741
|
}
|
|
33635
33742
|
const missing = wantedDownloads.filter((a) => !downloadedBytes.has(a.path));
|
|
33636
33743
|
if (missing.length) {
|
|
33637
|
-
p?.phase(`Recovering ${missing.length} file${missing.length === 1 ? "" : "s"} the bulk download dropped
|
|
33744
|
+
p?.phase(`Recovering ${missing.length} file${missing.length === 1 ? "" : "s"} the bulk download dropped...`);
|
|
33638
33745
|
for (const a of missing) {
|
|
33639
33746
|
const expectedSha = remote.get(a.path)?.sha256 ?? void 0;
|
|
33640
33747
|
let buf = null;
|
|
@@ -33676,7 +33783,7 @@ async function syncInner(projectGuid, root, ignore2, opts, interactive) {
|
|
|
33676
33783
|
if (currentBytes) {
|
|
33677
33784
|
mkdirSync4(dirname4(full), { recursive: true });
|
|
33678
33785
|
writeFileSync5(full, currentBytes);
|
|
33679
|
-
const stat2 =
|
|
33786
|
+
const stat2 = statSync2(full);
|
|
33680
33787
|
baseline.files[a.path] = {
|
|
33681
33788
|
size: stat2.size,
|
|
33682
33789
|
mtime: stat2.mtime.toISOString(),
|
|
@@ -33692,12 +33799,12 @@ async function syncInner(projectGuid, root, ignore2, opts, interactive) {
|
|
|
33692
33799
|
renamedRel,
|
|
33693
33800
|
{ expectedServerVersion: null }
|
|
33694
33801
|
);
|
|
33695
|
-
const stat2 =
|
|
33696
|
-
const { sha256
|
|
33802
|
+
const stat2 = statSync2(renamedFull);
|
|
33803
|
+
const { sha256 } = await hashFile(renamedFull);
|
|
33697
33804
|
baseline.files[renamedRel] = {
|
|
33698
33805
|
size: stat2.size,
|
|
33699
33806
|
mtime: stat2.mtime.toISOString(),
|
|
33700
|
-
sha256
|
|
33807
|
+
sha256,
|
|
33701
33808
|
serverVersion: result.serverVersion
|
|
33702
33809
|
};
|
|
33703
33810
|
} catch (e) {
|
|
@@ -33733,7 +33840,7 @@ async function syncInner(projectGuid, root, ignore2, opts, interactive) {
|
|
|
33733
33840
|
}
|
|
33734
33841
|
mkdirSync4(dirname4(full), { recursive: true });
|
|
33735
33842
|
writeFileSync5(full, buf);
|
|
33736
|
-
const stat2 =
|
|
33843
|
+
const stat2 = statSync2(full);
|
|
33737
33844
|
local.set(a.path, { size: stat2.size, mtime: stat2.mtime.toISOString(), sha256: void 0 });
|
|
33738
33845
|
baseline.files[a.path] = {
|
|
33739
33846
|
size: stat2.size,
|
|
@@ -33764,7 +33871,7 @@ async function syncInner(projectGuid, root, ignore2, opts, interactive) {
|
|
|
33764
33871
|
if (buf) {
|
|
33765
33872
|
mkdirSync4(dirname4(full), { recursive: true });
|
|
33766
33873
|
writeFileSync5(full, buf);
|
|
33767
|
-
const stat2 =
|
|
33874
|
+
const stat2 = statSync2(full);
|
|
33768
33875
|
baseline.files[a.path] = {
|
|
33769
33876
|
size: stat2.size,
|
|
33770
33877
|
mtime: stat2.mtime.toISOString(),
|
|
@@ -33778,12 +33885,12 @@ async function syncInner(projectGuid, root, ignore2, opts, interactive) {
|
|
|
33778
33885
|
const result = await uploadOneFile(config.projectGuid, renamed, a.renamedLocalTo, {
|
|
33779
33886
|
expectedServerVersion: null
|
|
33780
33887
|
});
|
|
33781
|
-
const stat2 =
|
|
33782
|
-
const { sha256
|
|
33888
|
+
const stat2 = statSync2(renamed);
|
|
33889
|
+
const { sha256 } = await hashFile(renamed);
|
|
33783
33890
|
baseline.files[a.renamedLocalTo] = {
|
|
33784
33891
|
size: stat2.size,
|
|
33785
33892
|
mtime: stat2.mtime.toISOString(),
|
|
33786
|
-
sha256
|
|
33893
|
+
sha256,
|
|
33787
33894
|
serverVersion: result.serverVersion
|
|
33788
33895
|
};
|
|
33789
33896
|
} catch (err) {
|
|
@@ -33817,14 +33924,14 @@ async function syncInner(projectGuid, root, ignore2, opts, interactive) {
|
|
|
33817
33924
|
continue;
|
|
33818
33925
|
}
|
|
33819
33926
|
try {
|
|
33820
|
-
const stat2 =
|
|
33927
|
+
const stat2 = statSync2(full);
|
|
33821
33928
|
if (stat2.size > UPLOAD_MAX_BYTES) {
|
|
33822
33929
|
errors.push(`Upload failed for ${a.path}: file exceeds the 30 GB upload limit`);
|
|
33823
33930
|
onBytes?.(a.localSize ?? 0);
|
|
33824
33931
|
continue;
|
|
33825
33932
|
}
|
|
33826
|
-
const
|
|
33827
|
-
prepared.push({ a, full, size: stat2.size, mtime: stat2.mtime.toISOString(), sha256
|
|
33933
|
+
const sha256 = local.get(a.path)?.sha256 ?? (await hashFile(full)).sha256;
|
|
33934
|
+
prepared.push({ a, full, size: stat2.size, mtime: stat2.mtime.toISOString(), sha256 });
|
|
33828
33935
|
} catch (e) {
|
|
33829
33936
|
errors.push(`Upload failed for ${a.path}: ${e.message}`);
|
|
33830
33937
|
onBytes?.(a.localSize ?? 0);
|
|
@@ -33971,13 +34078,13 @@ async function syncInner(projectGuid, root, ignore2, opts, interactive) {
|
|
|
33971
34078
|
const full = resolveInRoot(root, a.path);
|
|
33972
34079
|
mkdirSync4(dirname4(full), { recursive: true });
|
|
33973
34080
|
writeFileSync5(full, buf);
|
|
33974
|
-
const stat2 =
|
|
33975
|
-
const { sha256
|
|
34081
|
+
const stat2 = statSync2(full);
|
|
34082
|
+
const { sha256 } = await hashFile(full);
|
|
33976
34083
|
const current = typeof err.data?.current_server_version === "number" ? err.data.current_server_version : null;
|
|
33977
34084
|
baseline.files[a.path] = {
|
|
33978
34085
|
size: stat2.size,
|
|
33979
34086
|
mtime: stat2.mtime.toISOString(),
|
|
33980
|
-
sha256
|
|
34087
|
+
sha256,
|
|
33981
34088
|
serverVersion: current ?? a.expectedServerVersion ?? 0
|
|
33982
34089
|
};
|
|
33983
34090
|
errors.push(`Could not delete ${a.path}: server has a newer version - restored the server copy locally (delete it again and re-sync to confirm)`);
|
|
@@ -34021,7 +34128,7 @@ async function syncInner(projectGuid, root, ignore2, opts, interactive) {
|
|
|
34021
34128
|
function cleanupEmptyDirs(root, emptiedDirs) {
|
|
34022
34129
|
const isEmpty = (dir) => {
|
|
34023
34130
|
try {
|
|
34024
|
-
return
|
|
34131
|
+
return readdirSync4(dir).length === 0;
|
|
34025
34132
|
} catch {
|
|
34026
34133
|
return false;
|
|
34027
34134
|
}
|
|
@@ -34056,12 +34163,12 @@ async function pushFile(filePath) {
|
|
|
34056
34163
|
const result = await uploadOneFile(config.projectGuid, filePath, rel2, {
|
|
34057
34164
|
expectedServerVersion: baseEntry ? baseEntry.serverVersion : null
|
|
34058
34165
|
});
|
|
34059
|
-
const stat2 =
|
|
34060
|
-
const { sha256
|
|
34166
|
+
const stat2 = statSync2(filePath);
|
|
34167
|
+
const { sha256 } = await hashFile(filePath);
|
|
34061
34168
|
baseline.files[rel2] = {
|
|
34062
34169
|
size: stat2.size,
|
|
34063
34170
|
mtime: stat2.mtime.toISOString(),
|
|
34064
|
-
sha256
|
|
34171
|
+
sha256,
|
|
34065
34172
|
serverVersion: result.serverVersion
|
|
34066
34173
|
};
|
|
34067
34174
|
writeBaseline(baseline);
|
|
@@ -34249,11 +34356,11 @@ async function syncBeforeAction(opts) {
|
|
|
34249
34356
|
}
|
|
34250
34357
|
|
|
34251
34358
|
// src/helpers/body.ts
|
|
34252
|
-
import { readFileSync as
|
|
34359
|
+
import { readFileSync as readFileSync8 } from "node:fs";
|
|
34253
34360
|
function readStdin() {
|
|
34254
34361
|
if (process.stdin.isTTY) return "";
|
|
34255
34362
|
try {
|
|
34256
|
-
return
|
|
34363
|
+
return readFileSync8(0, "utf-8");
|
|
34257
34364
|
} catch {
|
|
34258
34365
|
return "";
|
|
34259
34366
|
}
|
|
@@ -34269,7 +34376,7 @@ function readFileField(spec) {
|
|
|
34269
34376
|
if (!field) throw new Error(`Invalid --file '${spec}': missing field name before '='.`);
|
|
34270
34377
|
if (!path5) throw new Error(`Invalid --file '${spec}': missing file path after '='.`);
|
|
34271
34378
|
try {
|
|
34272
|
-
return [field, { data:
|
|
34379
|
+
return [field, { data: readFileSync8(path5).toString("base64"), media_type: guessMime(path5) }];
|
|
34273
34380
|
} catch (e) {
|
|
34274
34381
|
throw new Error(`Cannot read --file '${path5}': ${e.message}`);
|
|
34275
34382
|
}
|
|
@@ -34300,7 +34407,7 @@ function resolveJsonBody(raw) {
|
|
|
34300
34407
|
} else if (raw.startsWith("@")) {
|
|
34301
34408
|
const path5 = raw.slice(1);
|
|
34302
34409
|
try {
|
|
34303
|
-
source =
|
|
34410
|
+
source = readFileSync8(path5, "utf-8");
|
|
34304
34411
|
} catch (e) {
|
|
34305
34412
|
throw new Error(`Cannot read body file '${path5}': ${e.message}`);
|
|
34306
34413
|
}
|
|
@@ -34381,23 +34488,23 @@ tokenCommand.command("revoke <short_guid>").alias("rm").description("Revoke an a
|
|
|
34381
34488
|
init_api();
|
|
34382
34489
|
init_config();
|
|
34383
34490
|
init_auth();
|
|
34384
|
-
import { basename, resolve as resolve7, dirname as dirname5 } from "path";
|
|
34385
|
-
import { existsSync as existsSync8, readFileSync as
|
|
34491
|
+
import { basename as basename2, resolve as resolve7, dirname as dirname5 } from "path";
|
|
34492
|
+
import { existsSync as existsSync8, readFileSync as readFileSync11 } from "fs";
|
|
34386
34493
|
init_colors();
|
|
34387
34494
|
init_utils();
|
|
34388
34495
|
|
|
34389
34496
|
// src/adopt-cwd.ts
|
|
34390
34497
|
init_api();
|
|
34391
|
-
import { readdirSync as
|
|
34392
|
-
import { join as
|
|
34498
|
+
import { readdirSync as readdirSync6, statSync as statSync4 } from "fs";
|
|
34499
|
+
import { join as join9, resolve as resolve6, sep as sep2, parse as parsePath } from "path";
|
|
34393
34500
|
import { homedir as homedir6 } from "os";
|
|
34394
34501
|
|
|
34395
34502
|
// src/project-setup.ts
|
|
34396
34503
|
init_config();
|
|
34397
34504
|
|
|
34398
34505
|
// src/template-vars.ts
|
|
34399
|
-
import { promises as fs, readdirSync as
|
|
34400
|
-
import { join as
|
|
34506
|
+
import { promises as fs, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
|
|
34507
|
+
import { join as join6, relative as relative2, extname as extname3 } from "path";
|
|
34401
34508
|
var SUBSTITUTABLE_EXTS = /* @__PURE__ */ new Set([
|
|
34402
34509
|
".html",
|
|
34403
34510
|
".htm",
|
|
@@ -34463,18 +34570,18 @@ function* walkTextFiles(root) {
|
|
|
34463
34570
|
const dir = stack.pop();
|
|
34464
34571
|
let entries;
|
|
34465
34572
|
try {
|
|
34466
|
-
entries =
|
|
34573
|
+
entries = readdirSync5(dir, { withFileTypes: true });
|
|
34467
34574
|
} catch {
|
|
34468
34575
|
continue;
|
|
34469
34576
|
}
|
|
34470
34577
|
for (const entry of entries) {
|
|
34471
34578
|
if (isSyncIgnored(entry.name)) continue;
|
|
34472
|
-
const full =
|
|
34579
|
+
const full = join6(dir, entry.name);
|
|
34473
34580
|
if (entry.isDirectory()) {
|
|
34474
34581
|
stack.push(full);
|
|
34475
34582
|
} else if (entry.isFile()) {
|
|
34476
34583
|
try {
|
|
34477
|
-
if (!
|
|
34584
|
+
if (!statSync3(full).isFile()) continue;
|
|
34478
34585
|
} catch {
|
|
34479
34586
|
continue;
|
|
34480
34587
|
}
|
|
@@ -34491,7 +34598,7 @@ async function substituteDir(dir, vars) {
|
|
|
34491
34598
|
for (const rel2 of walkTextFiles(dir)) {
|
|
34492
34599
|
const ext = extname3(rel2).toLowerCase();
|
|
34493
34600
|
if (!SUBSTITUTABLE_EXTS.has(ext)) continue;
|
|
34494
|
-
const abs =
|
|
34601
|
+
const abs = join6(dir, rel2);
|
|
34495
34602
|
let content;
|
|
34496
34603
|
try {
|
|
34497
34604
|
content = await fs.readFile(abs, "utf-8");
|
|
@@ -34557,18 +34664,18 @@ async function finalizeLocalProject(opts) {
|
|
|
34557
34664
|
}
|
|
34558
34665
|
|
|
34559
34666
|
// src/relay/paths.ts
|
|
34560
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as
|
|
34667
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
34561
34668
|
import { homedir as homedir4 } from "os";
|
|
34562
|
-
import { join as
|
|
34669
|
+
import { join as join7, resolve as resolve5 } from "path";
|
|
34563
34670
|
function getProjectsRoot() {
|
|
34564
|
-
const settingsPath =
|
|
34565
|
-
const defaultDir =
|
|
34671
|
+
const settingsPath = join7(homedir4(), ".gipity", "settings.json");
|
|
34672
|
+
const defaultDir = join7(homedir4(), "GipityProjects");
|
|
34566
34673
|
try {
|
|
34567
34674
|
if (existsSync6(settingsPath)) {
|
|
34568
|
-
const settings = JSON.parse(
|
|
34675
|
+
const settings = JSON.parse(readFileSync9(settingsPath, "utf-8"));
|
|
34569
34676
|
if (settings.projectsDir) return resolve5(settings.projectsDir);
|
|
34570
34677
|
} else {
|
|
34571
|
-
mkdirSync5(
|
|
34678
|
+
mkdirSync5(join7(homedir4(), ".gipity"), { recursive: true });
|
|
34572
34679
|
writeFileSync6(settingsPath, JSON.stringify({ projectsDir: defaultDir }, null, 2) + "\n");
|
|
34573
34680
|
}
|
|
34574
34681
|
} catch {
|
|
@@ -34577,11 +34684,11 @@ function getProjectsRoot() {
|
|
|
34577
34684
|
}
|
|
34578
34685
|
|
|
34579
34686
|
// src/relay/state.ts
|
|
34580
|
-
import { readFileSync as
|
|
34581
|
-
import { join as
|
|
34687
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync7, chmodSync as chmodSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
34688
|
+
import { join as join8 } from "path";
|
|
34582
34689
|
import { homedir as homedir5 } from "os";
|
|
34583
|
-
var RELAY_DIR = process.env.GIPITY_DIR ||
|
|
34584
|
-
var RELAY_FILE =
|
|
34690
|
+
var RELAY_DIR = process.env.GIPITY_DIR || join8(homedir5(), ".gipity");
|
|
34691
|
+
var RELAY_FILE = join8(RELAY_DIR, "relay.json");
|
|
34585
34692
|
var FILE_MODE = 384;
|
|
34586
34693
|
function emptyState() {
|
|
34587
34694
|
return { device: null, paused: false };
|
|
@@ -34589,7 +34696,7 @@ function emptyState() {
|
|
|
34589
34696
|
function loadState() {
|
|
34590
34697
|
if (!existsSync7(RELAY_FILE)) return emptyState();
|
|
34591
34698
|
try {
|
|
34592
|
-
const raw = JSON.parse(
|
|
34699
|
+
const raw = JSON.parse(readFileSync10(RELAY_FILE, "utf-8"));
|
|
34593
34700
|
return {
|
|
34594
34701
|
device: raw.device ?? null,
|
|
34595
34702
|
paused: Boolean(raw.paused),
|
|
@@ -34674,7 +34781,7 @@ function diagnosticsConsented() {
|
|
|
34674
34781
|
if (env && env !== "0" && env.toLowerCase() !== "false") return false;
|
|
34675
34782
|
return loadState().diagnostics_consent !== false;
|
|
34676
34783
|
}
|
|
34677
|
-
var RELAY_PID_FILE =
|
|
34784
|
+
var RELAY_PID_FILE = join8(RELAY_DIR, "relay.pid");
|
|
34678
34785
|
function getDaemonPidPath() {
|
|
34679
34786
|
return RELAY_PID_FILE;
|
|
34680
34787
|
}
|
|
@@ -34691,7 +34798,7 @@ function clearDaemonPid() {
|
|
|
34691
34798
|
function isDaemonRunning() {
|
|
34692
34799
|
if (!existsSync7(RELAY_PID_FILE)) return false;
|
|
34693
34800
|
try {
|
|
34694
|
-
const raw =
|
|
34801
|
+
const raw = readFileSync10(RELAY_PID_FILE, "utf-8").trim();
|
|
34695
34802
|
const pid = parseInt(raw, 10);
|
|
34696
34803
|
if (!pid || isNaN(pid)) {
|
|
34697
34804
|
try {
|
|
@@ -34731,17 +34838,17 @@ function scanForAdoption(cwd) {
|
|
|
34731
34838
|
if (truncated) return;
|
|
34732
34839
|
let entries;
|
|
34733
34840
|
try {
|
|
34734
|
-
entries =
|
|
34841
|
+
entries = readdirSync6(dir);
|
|
34735
34842
|
} catch {
|
|
34736
34843
|
return;
|
|
34737
34844
|
}
|
|
34738
34845
|
for (const name of entries) {
|
|
34739
34846
|
if (truncated) return;
|
|
34740
34847
|
if (isSyncIgnored(name)) continue;
|
|
34741
|
-
const full =
|
|
34848
|
+
const full = join9(dir, name);
|
|
34742
34849
|
let st2;
|
|
34743
34850
|
try {
|
|
34744
|
-
st2 =
|
|
34851
|
+
st2 = statSync4(full);
|
|
34745
34852
|
} catch {
|
|
34746
34853
|
continue;
|
|
34747
34854
|
}
|
|
@@ -34767,7 +34874,7 @@ function scanForAdoption(cwd) {
|
|
|
34767
34874
|
function isLikelyEmpty(cwd) {
|
|
34768
34875
|
let entries;
|
|
34769
34876
|
try {
|
|
34770
|
-
entries =
|
|
34877
|
+
entries = readdirSync6(cwd);
|
|
34771
34878
|
} catch {
|
|
34772
34879
|
return true;
|
|
34773
34880
|
}
|
|
@@ -34805,18 +34912,18 @@ function canAdoptCwd(cwd) {
|
|
|
34805
34912
|
function countGitRepoChildren(dir) {
|
|
34806
34913
|
let entries;
|
|
34807
34914
|
try {
|
|
34808
|
-
entries =
|
|
34915
|
+
entries = readdirSync6(dir);
|
|
34809
34916
|
} catch {
|
|
34810
34917
|
return 0;
|
|
34811
34918
|
}
|
|
34812
34919
|
let count = 0;
|
|
34813
34920
|
for (const name of entries.slice(0, 50)) {
|
|
34814
34921
|
if (name.startsWith(".")) continue;
|
|
34815
|
-
const child =
|
|
34922
|
+
const child = join9(dir, name);
|
|
34816
34923
|
try {
|
|
34817
|
-
if (!
|
|
34818
|
-
const gitPath =
|
|
34819
|
-
if (
|
|
34924
|
+
if (!statSync4(child).isDirectory()) continue;
|
|
34925
|
+
const gitPath = join9(child, ".git");
|
|
34926
|
+
if (statSync4(gitPath)) count++;
|
|
34820
34927
|
} catch {
|
|
34821
34928
|
}
|
|
34822
34929
|
if (count >= 3) return count;
|
|
@@ -34830,7 +34937,7 @@ function formatCwdLabel(cwd) {
|
|
|
34830
34937
|
if (norm.startsWith(home + sep2)) {
|
|
34831
34938
|
const tail = norm.slice(home.length + 1).split(sep2);
|
|
34832
34939
|
if (tail.length <= 2) return "~/" + tail.join("/");
|
|
34833
|
-
return "
|
|
34940
|
+
return "~/.../" + tail.slice(-2).join("/");
|
|
34834
34941
|
}
|
|
34835
34942
|
const parts = norm.split(sep2).filter(Boolean);
|
|
34836
34943
|
if (parts.length <= 1) return norm;
|
|
@@ -34995,7 +35102,7 @@ Working with an existing Gipity project:
|
|
|
34995
35102
|
process.exit(1);
|
|
34996
35103
|
}
|
|
34997
35104
|
}
|
|
34998
|
-
const projectName = (name ||
|
|
35105
|
+
const projectName = (name || basename2(cwd)).slice(0, 100);
|
|
34999
35106
|
const projectSlug = slugify(projectName);
|
|
35000
35107
|
if (!projectSlug) {
|
|
35001
35108
|
console.error(error("Could not derive a valid project slug. Provide a name: gipity init my-app"));
|
|
@@ -35037,7 +35144,7 @@ Working with an existing Gipity project:
|
|
|
35037
35144
|
if (adopted.applied > 0) console.log(`Synced ${adopted.applied} change${adopted.applied > 1 ? "s" : ""} with Gipity.`);
|
|
35038
35145
|
if (opts.capture === false) {
|
|
35039
35146
|
try {
|
|
35040
|
-
const cfg = JSON.parse(
|
|
35147
|
+
const cfg = JSON.parse(readFileSync11(resolve7(cwd, ".gipity.json"), "utf-8"));
|
|
35041
35148
|
cfg.captureHooks = false;
|
|
35042
35149
|
saveConfigAt(cwd, cfg);
|
|
35043
35150
|
} catch {
|
|
@@ -35071,15 +35178,15 @@ init_auth();
|
|
|
35071
35178
|
init_api();
|
|
35072
35179
|
init_config();
|
|
35073
35180
|
init_colors();
|
|
35074
|
-
import { existsSync as existsSync9, readFileSync as
|
|
35075
|
-
import { join as
|
|
35181
|
+
import { existsSync as existsSync9, readFileSync as readFileSync12 } from "fs";
|
|
35182
|
+
import { join as join10, resolve as resolve8 } from "path";
|
|
35076
35183
|
import { homedir as homedir7 } from "os";
|
|
35077
35184
|
function checkGipityPlugin() {
|
|
35078
|
-
const path5 =
|
|
35185
|
+
const path5 = join10(homedir7(), ".claude", "settings.json");
|
|
35079
35186
|
let settings = {};
|
|
35080
35187
|
if (existsSync9(path5)) {
|
|
35081
35188
|
try {
|
|
35082
|
-
settings = JSON.parse(
|
|
35189
|
+
settings = JSON.parse(readFileSync12(path5, "utf-8"));
|
|
35083
35190
|
} catch {
|
|
35084
35191
|
}
|
|
35085
35192
|
}
|
|
@@ -35092,12 +35199,14 @@ function checkGipityPlugin() {
|
|
|
35092
35199
|
return { missing, ok: missing.length === 0, stale };
|
|
35093
35200
|
}
|
|
35094
35201
|
async function probeAuth(loggedIn) {
|
|
35095
|
-
if (!loggedIn && !usingEnvToken()) return "none";
|
|
35096
|
-
if (!usingEnvToken() && sessionExpired()) return "expired";
|
|
35097
|
-
const timeout = new Promise(
|
|
35202
|
+
if (!loggedIn && !usingEnvToken()) return { state: "none", account: null };
|
|
35203
|
+
if (!usingEnvToken() && sessionExpired()) return { state: "expired", account: null };
|
|
35204
|
+
const timeout = new Promise(
|
|
35205
|
+
(res) => setTimeout(() => res({ state: "unreachable", account: null }), 5e3).unref?.()
|
|
35206
|
+
);
|
|
35098
35207
|
const call = get("/users/me").then(
|
|
35099
|
-
() => "ok",
|
|
35100
|
-
(err) => err instanceof ApiError && err.statusCode === 401 ? "rejected" : "unreachable"
|
|
35208
|
+
(res) => ({ state: "ok", account: res.data?.accountSlug ?? null }),
|
|
35209
|
+
(err) => ({ state: err instanceof ApiError && err.statusCode === 401 ? "rejected" : "unreachable", account: null })
|
|
35101
35210
|
);
|
|
35102
35211
|
return Promise.race([call, timeout]);
|
|
35103
35212
|
}
|
|
@@ -35109,6 +35218,8 @@ var statusCommand = new Command("status").alias("whoami").description("Show proj
|
|
|
35109
35218
|
const hookCheck = config ? checkGipityPlugin() : null;
|
|
35110
35219
|
const queueDelivered = auth && !sessionExpired() ? await flushBugQueue().catch(() => 0) : 0;
|
|
35111
35220
|
const probe = await probeAuth(!!auth);
|
|
35221
|
+
const accountMismatch = probe.state === "ok" && !!config && !!probe.account && probe.account !== config.accountSlug;
|
|
35222
|
+
const apiBaseInUse = resolveApiBase();
|
|
35112
35223
|
if (opts.json) {
|
|
35113
35224
|
console.log(JSON.stringify({
|
|
35114
35225
|
project: config ? {
|
|
@@ -35116,17 +35227,21 @@ var statusCommand = new Command("status").alias("whoami").description("Show proj
|
|
|
35116
35227
|
slug: config.projectSlug,
|
|
35117
35228
|
account: config.accountSlug,
|
|
35118
35229
|
apiBase: config.apiBase,
|
|
35230
|
+
apiBaseInUse,
|
|
35119
35231
|
url: liveUrl(config)
|
|
35120
35232
|
} : null,
|
|
35121
35233
|
// `valid` reflects the refresh token (the real session) - access
|
|
35122
35234
|
// tokens auto-renew, so their expiry must not read as "invalid".
|
|
35123
35235
|
// `probe` is what one live call just proved: 'rejected' means every
|
|
35124
35236
|
// authenticated command will fail even though `valid` reads true.
|
|
35237
|
+
// 'mismatch' overrides 'ok' when the live account isn't the one that
|
|
35238
|
+
// owns this project - `valid` still reads true (the token IS valid).
|
|
35125
35239
|
auth: auth || usingEnvToken() ? {
|
|
35126
35240
|
email: auth?.email,
|
|
35241
|
+
account: probe.account,
|
|
35127
35242
|
source: usingEnvToken() ? "agent-token" : "session",
|
|
35128
|
-
valid: usingEnvToken() ? probe !== "rejected" : !sessionExpired(),
|
|
35129
|
-
probe
|
|
35243
|
+
valid: usingEnvToken() ? probe.state !== "rejected" : !sessionExpired(),
|
|
35244
|
+
probe: accountMismatch ? "mismatch" : probe.state
|
|
35130
35245
|
} : null,
|
|
35131
35246
|
plugin: hookCheck
|
|
35132
35247
|
}, null, 2));
|
|
@@ -35139,18 +35254,25 @@ var statusCommand = new Command("status").alias("whoami").description("Show proj
|
|
|
35139
35254
|
console.log(`${muted("Account:")} ${config.accountSlug}`);
|
|
35140
35255
|
console.log(`${muted("Live:")} ${liveUrl(config)}`);
|
|
35141
35256
|
console.log(`${muted("API:")} ${config.apiBase}`);
|
|
35257
|
+
if (apiBaseInUse !== config.apiBase) {
|
|
35258
|
+
console.log(`${muted("API (in use):")} ${warning(apiBaseInUse)} ${muted("(overrides .gipity.json \u2014 GIPITY_API_BASE / --api-base / allowlist)")}`);
|
|
35259
|
+
}
|
|
35142
35260
|
if (config.agentGuid) console.log(`${muted("Agent:")} ${config.agentGuid}`);
|
|
35143
35261
|
}
|
|
35144
35262
|
if (usingEnvToken()) {
|
|
35145
|
-
console.log(`${muted("Auth:")} ${probe === "rejected" ? warning("agent API token (GIPITY_TOKEN) rejected by the server \u2014 mint a new one: gipity skill read agent-deploy") : success("agent API token (GIPITY_TOKEN)")}${probe === "unreachable" ? ` ${muted("(unverified \u2014 API unreachable)")}` : ""}`);
|
|
35263
|
+
console.log(`${muted("Auth:")} ${probe.state === "rejected" ? warning("agent API token (GIPITY_TOKEN) rejected by the server \u2014 mint a new one: gipity skill read agent-deploy") : success("agent API token (GIPITY_TOKEN)")}${probe.state === "unreachable" ? ` ${muted("(unverified \u2014 API unreachable)")}` : ""}`);
|
|
35146
35264
|
} else if (!auth) {
|
|
35147
35265
|
console.log(`${muted("Auth:")} ${warning("not logged in. Run: gipity login")}`);
|
|
35148
|
-
} else if (probe === "expired") {
|
|
35266
|
+
} else if (probe.state === "expired") {
|
|
35149
35267
|
console.log(`${muted("Auth:")} ${warning(`session expired for ${auth.email}. Run: gipity login (headless/CI: set GIPITY_TOKEN \u2014 gipity skill read agent-deploy)`)}`);
|
|
35150
|
-
} else if (probe === "rejected") {
|
|
35268
|
+
} else if (probe.state === "rejected") {
|
|
35151
35269
|
console.log(`${muted("Auth:")} ${warning(`session for ${auth.email} was rejected by the server. Run: gipity login (headless/CI: set GIPITY_TOKEN \u2014 gipity skill read agent-deploy)`)}`);
|
|
35152
35270
|
} else {
|
|
35153
|
-
console.log(`${muted("Auth:")} ${success(auth.email)}${probe === "unreachable" ? ` ${muted("(unverified \u2014 API unreachable)")}` : ""}`);
|
|
35271
|
+
console.log(`${muted("Auth:")} ${success(auth.email)}${probe.state === "unreachable" ? ` ${muted("(unverified \u2014 API unreachable)")}` : ""}`);
|
|
35272
|
+
}
|
|
35273
|
+
if (accountMismatch) {
|
|
35274
|
+
console.log(`${muted("Account:")} ${warning(`logged-in account (${probe.account}) differs from this project's account (${config.accountSlug}). If you didn't expect this you may be logged into the wrong account \u2014 run: gipity login`)}`);
|
|
35275
|
+
console.log(muted("(If this project was shared with you via gipity rbac, this is expected.)"));
|
|
35154
35276
|
}
|
|
35155
35277
|
if (queueDelivered > 0) {
|
|
35156
35278
|
console.log(`${muted("Bug queue:")} ${success(`delivered ${queueDelivered} queued bug report${queueDelivered === 1 ? "" : "s"}`)}`);
|
|
@@ -35181,7 +35303,10 @@ var statusCommand = new Command("status").alias("whoami").description("Show proj
|
|
|
35181
35303
|
});
|
|
35182
35304
|
|
|
35183
35305
|
// src/commands/sync.ts
|
|
35306
|
+
import { dirname as dirname6 } from "node:path";
|
|
35184
35307
|
init_colors();
|
|
35308
|
+
init_utils();
|
|
35309
|
+
init_config();
|
|
35185
35310
|
var syncCommand = new Command("sync").description("Sync files").addHelpText("after", "\nAdd a .gipityignore at the project root to exclude paths (gitignore-style).").option("--plan", "Print the plan without applying any changes").option("--force", "Bypass the bulk-deletion guard").option("--prune", "Remove files that exist on Gipity but not locally (applies the bulk deletes the guard defers)").option("--json", "Output as JSON").action(async (opts) => {
|
|
35186
35311
|
try {
|
|
35187
35312
|
const progress = opts.json ? void 0 : createProgressReporter();
|
|
@@ -35194,6 +35319,16 @@ var syncCommand = new Command("sync").description("Sync files").addHelpText("aft
|
|
|
35194
35319
|
console.log(`
|
|
35195
35320
|
${result.applied} action${result.applied > 1 ? "s" : ""} applied.`);
|
|
35196
35321
|
}
|
|
35322
|
+
if (isWsl()) {
|
|
35323
|
+
const configPath = getConfigPath();
|
|
35324
|
+
const twin = configPath ? findWindowsTwinProject(dirname6(configPath)) : null;
|
|
35325
|
+
if (twin) {
|
|
35326
|
+
console.log(muted(
|
|
35327
|
+
`
|
|
35328
|
+
Note: a Windows-side copy of this project exists at ${wslPathToWindows(twin)} and is NOT synced. If you added files there, move them into this folder so they sync.`
|
|
35329
|
+
));
|
|
35330
|
+
}
|
|
35331
|
+
}
|
|
35197
35332
|
if (result.deferredDeletes > 0) {
|
|
35198
35333
|
console.log(muted(
|
|
35199
35334
|
`
|
|
@@ -35241,8 +35376,8 @@ var pushCommand = new Command("push").description("Push one or more files").argu
|
|
|
35241
35376
|
// src/commands/upload.ts
|
|
35242
35377
|
init_api();
|
|
35243
35378
|
init_config();
|
|
35244
|
-
import { statSync as
|
|
35245
|
-
import { basename as
|
|
35379
|
+
import { statSync as statSync5 } from "fs";
|
|
35380
|
+
import { basename as basename3 } from "path";
|
|
35246
35381
|
init_utils();
|
|
35247
35382
|
init_colors();
|
|
35248
35383
|
var uploadCommand = new Command("upload").description(`Upload a local file and print a durable, worker-reachable URL for it.
|
|
@@ -35259,14 +35394,14 @@ token-signed serve url instead (reachable only by holders of the url).`).argumen
|
|
|
35259
35394
|
const { config } = await resolveProjectContext();
|
|
35260
35395
|
let size;
|
|
35261
35396
|
try {
|
|
35262
|
-
const st2 =
|
|
35397
|
+
const st2 = statSync5(file);
|
|
35263
35398
|
if (!st2.isFile()) throw new Error("not a regular file");
|
|
35264
35399
|
size = st2.size;
|
|
35265
35400
|
} catch (err) {
|
|
35266
35401
|
throw new Error(`can't read ${file}: ${err.message}`);
|
|
35267
35402
|
}
|
|
35268
35403
|
if (size === 0) throw new Error(`${file} is empty - nothing to upload.`);
|
|
35269
|
-
const filename =
|
|
35404
|
+
const filename = basename3(file);
|
|
35270
35405
|
const contentType = opts.contentType || guessMime(file);
|
|
35271
35406
|
const doUpload = async () => {
|
|
35272
35407
|
const init = await post(`/api/${config.projectGuid}/uploads/init`, {
|
|
@@ -35283,7 +35418,7 @@ token-signed serve url instead (reachable only by holders of the url).`).argumen
|
|
|
35283
35418
|
const comp = await post(`/api/${config.projectGuid}/uploads/complete`, completeBody);
|
|
35284
35419
|
return comp.data;
|
|
35285
35420
|
};
|
|
35286
|
-
const data = opts.json ? await doUpload() : await withSpinner(`Uploading ${filename} (${formatSize(size)})
|
|
35421
|
+
const data = opts.json ? await doUpload() : await withSpinner(`Uploading ${filename} (${formatSize(size)})...`, doUpload, { done: null });
|
|
35287
35422
|
if (opts.json) {
|
|
35288
35423
|
console.log(JSON.stringify(data));
|
|
35289
35424
|
return;
|
|
@@ -35307,7 +35442,7 @@ init_colors();
|
|
|
35307
35442
|
init_auth();
|
|
35308
35443
|
|
|
35309
35444
|
// src/commands/page-eval.ts
|
|
35310
|
-
import { readFileSync as
|
|
35445
|
+
import { readFileSync as readFileSync14 } from "node:fs";
|
|
35311
35446
|
init_api();
|
|
35312
35447
|
init_colors();
|
|
35313
35448
|
init_auth();
|
|
@@ -35316,8 +35451,8 @@ init_config();
|
|
|
35316
35451
|
// src/page-fixtures.ts
|
|
35317
35452
|
init_api();
|
|
35318
35453
|
init_config();
|
|
35319
|
-
import { readFileSync as
|
|
35320
|
-
import { basename as
|
|
35454
|
+
import { readFileSync as readFileSync13, statSync as statSync6, existsSync as existsSync10, readdirSync as readdirSync7 } from "node:fs";
|
|
35455
|
+
import { basename as basename4, resolve as resolve10, relative as relative3, join as join11 } from "node:path";
|
|
35321
35456
|
var FIND_SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", ".gipity", "dist", "build", ".next", "coverage"]);
|
|
35322
35457
|
function findByBasename(root, name, limit = 3) {
|
|
35323
35458
|
const hits = [];
|
|
@@ -35328,15 +35463,15 @@ function findByBasename(root, name, limit = 3) {
|
|
|
35328
35463
|
visited++;
|
|
35329
35464
|
let entries;
|
|
35330
35465
|
try {
|
|
35331
|
-
entries =
|
|
35466
|
+
entries = readdirSync7(dir, { withFileTypes: true });
|
|
35332
35467
|
} catch {
|
|
35333
35468
|
continue;
|
|
35334
35469
|
}
|
|
35335
35470
|
for (const e of entries) {
|
|
35336
35471
|
if (e.isDirectory()) {
|
|
35337
|
-
if (!FIND_SKIP.has(e.name)) queue.push(
|
|
35472
|
+
if (!FIND_SKIP.has(e.name)) queue.push(join11(dir, e.name));
|
|
35338
35473
|
} else if (e.name === name && hits.length < limit) {
|
|
35339
|
-
hits.push(
|
|
35474
|
+
hits.push(join11(dir, e.name));
|
|
35340
35475
|
}
|
|
35341
35476
|
}
|
|
35342
35477
|
}
|
|
@@ -35346,19 +35481,19 @@ function assertLocalAsset(flag, localPath) {
|
|
|
35346
35481
|
const abs = resolve10(localPath);
|
|
35347
35482
|
if (existsSync10(abs)) return;
|
|
35348
35483
|
const root = getProjectRoot() ?? process.cwd();
|
|
35349
|
-
const elsewhere = findByBasename(root,
|
|
35484
|
+
const elsewhere = findByBasename(root, basename4(localPath)).filter((p) => p !== abs);
|
|
35350
35485
|
const found = elsewhere.length ? `
|
|
35351
35486
|
That file DOES exist here \u2014 pass this path instead:
|
|
35352
35487
|
${elsewhere.map((p) => ` ${flag} ${relative3(process.cwd(), p) || p}`).join("\n")}` : `
|
|
35353
|
-
Nothing named "${
|
|
35488
|
+
Nothing named "${basename4(localPath)}" under ${root} either. Generate a frame with \`gipity generate image "<description>" -o ${localPath}\`, or check the path.`;
|
|
35354
35489
|
throw new Error(
|
|
35355
35490
|
`${flag} ${localPath}: no such file \u2014 looked for ${abs} (relative paths resolve against the current directory, ${process.cwd()}).${found}`
|
|
35356
35491
|
);
|
|
35357
35492
|
}
|
|
35358
35493
|
async function uploadPublicFixture(projectGuid, localPath, flag = "--fixture") {
|
|
35359
35494
|
assertLocalAsset(flag, localPath);
|
|
35360
|
-
const name =
|
|
35361
|
-
const size =
|
|
35495
|
+
const name = basename4(localPath);
|
|
35496
|
+
const size = statSync6(localPath).size;
|
|
35362
35497
|
const contentType = guessMime(localPath);
|
|
35363
35498
|
const init = await post(
|
|
35364
35499
|
`/api/${projectGuid}/uploads/init`,
|
|
@@ -35370,7 +35505,7 @@ async function uploadPublicFixture(projectGuid, localPath, flag = "--fixture") {
|
|
|
35370
35505
|
const res = await fetch(init.data.url, {
|
|
35371
35506
|
method: "PUT",
|
|
35372
35507
|
headers: { "Content-Type": contentType },
|
|
35373
|
-
body:
|
|
35508
|
+
body: readFileSync13(localPath)
|
|
35374
35509
|
});
|
|
35375
35510
|
if (!res.ok) {
|
|
35376
35511
|
throw new Error(`upload of fixture "${name}" failed: ${res.status} ${res.statusText}`);
|
|
@@ -35390,7 +35525,7 @@ function assertCameraFile(localPath) {
|
|
|
35390
35525
|
const ext = localPath.slice(localPath.lastIndexOf(".")).toLowerCase();
|
|
35391
35526
|
if (CAMERA_EXTS.includes(ext)) return;
|
|
35392
35527
|
throw new Error(
|
|
35393
|
-
`--camera ${
|
|
35528
|
+
`--camera ${basename4(localPath)}: unsupported file type "${ext || "(none)"}" \u2014 the camera feed must be an image or video (${CAMERA_EXTS.join(", ")}).
|
|
35394
35529
|
A still image is played as a looping single-frame feed, which is what a gesture/pose/object model needs.
|
|
35395
35530
|
No frame to hand? Generate one: gipity generate image "a hand making a closed fist, palm to camera, plain background"`
|
|
35396
35531
|
);
|
|
@@ -35443,7 +35578,7 @@ function summarizeExpr(expr) {
|
|
|
35443
35578
|
const oneLine = meaningful.length <= 1;
|
|
35444
35579
|
if (oneLine && expr.trim().length <= EXPR_ECHO_MAX_CHARS) return expr.trim();
|
|
35445
35580
|
const first = (meaningful[0] ?? "").trim();
|
|
35446
|
-
const head = first.length > EXPR_ECHO_MAX_CHARS ? `${first.slice(0, EXPR_ECHO_MAX_CHARS -
|
|
35581
|
+
const head = first.length > EXPR_ECHO_MAX_CHARS ? `${first.slice(0, EXPR_ECHO_MAX_CHARS - 3)}...` : first;
|
|
35447
35582
|
const shape = oneLine ? `(${expr.trim().length} chars)` : `(+${meaningful.length - 1} more ${meaningful.length - 1 === 1 ? "line" : "lines"}, ${expr.trim().length} chars)`;
|
|
35448
35583
|
return `${head} ${shape}`;
|
|
35449
35584
|
}
|
|
@@ -35470,7 +35605,7 @@ function capWaitForTimeoutMs(raw) {
|
|
|
35470
35605
|
return WAIT_FOR_MAX_MS;
|
|
35471
35606
|
}
|
|
35472
35607
|
function readScriptFile(pathArg) {
|
|
35473
|
-
return
|
|
35608
|
+
return readFileSync14(pathArg === "-" ? 0 : pathArg, "utf8");
|
|
35474
35609
|
}
|
|
35475
35610
|
function parsesAsExpression(s) {
|
|
35476
35611
|
try {
|
|
@@ -35566,7 +35701,7 @@ function evalExecTimeoutMessage(result, budgetMs) {
|
|
|
35566
35701
|
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.`;
|
|
35567
35702
|
}
|
|
35568
35703
|
var JS_DECOY_FLAGS = ["--js", "--javascript", "--script", "--code", "--expr", "--eval", "--exec", "--action"];
|
|
35569
|
-
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), or --file - to read it from stdin (pipe a heredoc: --file - <<'EOF'
|
|
35704
|
+
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), or --file - to read it from stdin (pipe a heredoc: --file - <<'EOF' ... EOF) with no tmp file. Runs as an async function body, so top-level return/await work. Same post-load budget as <expr> (--timeout).`).option(
|
|
35570
35705
|
"--step <expr>",
|
|
35571
35706
|
`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.`,
|
|
35572
35707
|
(val, prev) => [...prev, val],
|
|
@@ -35610,7 +35745,7 @@ var pageEvalCommand = new Command("eval").description("Evaluate JS in a real bro
|
|
|
35610
35745
|
try {
|
|
35611
35746
|
expr = readScriptFile(opts.file);
|
|
35612
35747
|
} catch {
|
|
35613
|
-
pageEvalCommand.error(opts.file === "-" ? `error: --file - reads the script from stdin, but stdin was empty/unreadable
|
|
35748
|
+
pageEvalCommand.error(opts.file === "-" ? `error: --file - reads the script from stdin, but stdin was empty/unreadable - pipe it in, e.g. gipity page eval "<url>" --file - <<'EOF' ... EOF` : `error: Cannot read file: ${opts.file}`);
|
|
35614
35749
|
}
|
|
35615
35750
|
}
|
|
35616
35751
|
if (opts.reload !== void 0 && opts.reloadFile) {
|
|
@@ -35672,12 +35807,12 @@ var pageEvalCommand = new Command("eval").description("Evaluate JS in a real bro
|
|
|
35672
35807
|
projectGuid = config.projectGuid;
|
|
35673
35808
|
}
|
|
35674
35809
|
if (opts.camera) {
|
|
35675
|
-
console.log(muted(`Hosting camera feed ${opts.camera}
|
|
35810
|
+
console.log(muted(`Hosting camera feed ${opts.camera}...`));
|
|
35676
35811
|
camera = await uploadCameraFeed(projectGuid, opts.camera);
|
|
35677
35812
|
}
|
|
35678
35813
|
if (fixturePaths.length) {
|
|
35679
35814
|
for (const p of fixturePaths) {
|
|
35680
|
-
console.log(muted(`Hosting fixture ${p}
|
|
35815
|
+
console.log(muted(`Hosting fixture ${p}...`));
|
|
35681
35816
|
hosted.push(await uploadPublicFixture(projectGuid, p));
|
|
35682
35817
|
}
|
|
35683
35818
|
const map = {};
|
|
@@ -35877,7 +36012,7 @@ init_auth();
|
|
|
35877
36012
|
init_colors();
|
|
35878
36013
|
init_utils();
|
|
35879
36014
|
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
35880
|
-
import { dirname as
|
|
36015
|
+
import { dirname as dirname7, join as join12, resolve as resolvePath } from "path";
|
|
35881
36016
|
var DEVICE_PRESETS = {
|
|
35882
36017
|
default: { width: 1280, height: 720 },
|
|
35883
36018
|
desktop: { width: 1920, height: 1080 },
|
|
@@ -35941,7 +36076,7 @@ function dimSuffix(vp2) {
|
|
|
35941
36076
|
}
|
|
35942
36077
|
function defaultScreenshotDir() {
|
|
35943
36078
|
const root = getProjectRoot();
|
|
35944
|
-
return
|
|
36079
|
+
return join12(root ?? ".", "screenshots");
|
|
35945
36080
|
}
|
|
35946
36081
|
function timestampSlug(d = /* @__PURE__ */ new Date()) {
|
|
35947
36082
|
const p = (n) => String(n).padStart(2, "0");
|
|
@@ -36004,7 +36139,7 @@ var WAIT_FOR_MAX_MS2 = 15e3;
|
|
|
36004
36139
|
var WAIT_FOR_DEFAULT_MS = 15e3;
|
|
36005
36140
|
var MAX_POST_LOAD_DELAY_MS = 3e4;
|
|
36006
36141
|
var CAMERA_DEFAULT_DELAY_MS = 15e3;
|
|
36007
|
-
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
|
|
36142
|
+
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 - 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('./...') 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("--file <path>", "Read the pre-capture script from a file instead of inline --action (mutually exclusive), or --file - to read it from stdin (pipe a heredoc: --file - <<'EOF' ... EOF) with no tmp file. For a multi-step driver - click through a flow, wait for it to render - then capture. Same async-function-body semantics as --action; same --file flag as page eval.").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 () => {
|
|
36008
36143
|
const aliasFlag = ACTION_ALIAS_FLAGS.find((f) => opts[f.slice(2)] !== void 0);
|
|
36009
36144
|
if (aliasFlag) {
|
|
36010
36145
|
console.error(muted(
|
|
@@ -36020,7 +36155,7 @@ var pageScreenshotCommand = new Command("screenshot").description("Screenshot a
|
|
|
36020
36155
|
try {
|
|
36021
36156
|
actionScript = readScriptFile(opts.file);
|
|
36022
36157
|
} catch {
|
|
36023
|
-
throw new Error(opts.file === "-" ? `--file - reads the pre-capture script from stdin, but stdin was empty/unreadable
|
|
36158
|
+
throw new Error(opts.file === "-" ? `--file - reads the pre-capture script from stdin, but stdin was empty/unreadable - pipe it in, e.g. gipity page screenshot "<url>" --file - <<'EOF' ... EOF` : `Cannot read file: ${opts.file}`);
|
|
36024
36159
|
}
|
|
36025
36160
|
}
|
|
36026
36161
|
const delayRaw = opts.wait ?? opts.postLoadDelay;
|
|
@@ -36088,7 +36223,7 @@ var pageScreenshotCommand = new Command("screenshot").description("Screenshot a
|
|
|
36088
36223
|
if (opts.camera) {
|
|
36089
36224
|
assertCameraFile(opts.camera);
|
|
36090
36225
|
if (!projectGuid) throw new Error("--camera needs a linked project (the frame is hosted for the browser to fetch) \u2014 run `gipity link` first.");
|
|
36091
|
-
console.log(muted(`Hosting camera feed ${opts.camera}
|
|
36226
|
+
console.log(muted(`Hosting camera feed ${opts.camera}...`));
|
|
36092
36227
|
camera = await uploadCameraFeed(projectGuid, opts.camera);
|
|
36093
36228
|
}
|
|
36094
36229
|
const preCapture = [
|
|
@@ -36113,7 +36248,7 @@ var pageScreenshotCommand = new Command("screenshot").description("Screenshot a
|
|
|
36113
36248
|
let entries;
|
|
36114
36249
|
try {
|
|
36115
36250
|
try {
|
|
36116
|
-
entries = opts.json ? await doShoot() : await withSpinner("Capturing
|
|
36251
|
+
entries = opts.json ? await doShoot() : await withSpinner("Capturing...", doShoot, { done: null });
|
|
36117
36252
|
} catch (err) {
|
|
36118
36253
|
throw preCapture ? new Error(augmentSandboxTimeout(err.message)) : err;
|
|
36119
36254
|
}
|
|
@@ -36136,8 +36271,8 @@ var pageScreenshotCommand = new Command("screenshot").description("Screenshot a
|
|
|
36136
36271
|
const dir = defaultScreenshotDir();
|
|
36137
36272
|
const savedFiles = [];
|
|
36138
36273
|
for (let i = 0; i < pngs.length; i++) {
|
|
36139
|
-
const target = opts.output ? opts.output :
|
|
36140
|
-
mkdirSync7(
|
|
36274
|
+
const target = opts.output ? opts.output : join12(dir, names[i] ?? shotName(meta.screenshots[i].viewport));
|
|
36275
|
+
mkdirSync7(dirname7(target), { recursive: true });
|
|
36141
36276
|
writeFileSync8(target, pngs[i].buffer);
|
|
36142
36277
|
savedFiles.push(resolvePath(target));
|
|
36143
36278
|
}
|
|
@@ -36265,10 +36400,10 @@ function shortUrl(url, truncate = true, maxLen = 100) {
|
|
|
36265
36400
|
result = url;
|
|
36266
36401
|
}
|
|
36267
36402
|
if (!truncate || result.length <= maxLen) return result;
|
|
36268
|
-
const keep = maxLen -
|
|
36403
|
+
const keep = maxLen - 3;
|
|
36269
36404
|
const headLen = Math.ceil(keep / 2);
|
|
36270
36405
|
const tailLen = Math.floor(keep / 2);
|
|
36271
|
-
return result.slice(0, headLen) + "
|
|
36406
|
+
return result.slice(0, headLen) + "..." + result.slice(-tailLen);
|
|
36272
36407
|
}
|
|
36273
36408
|
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) => {
|
|
36274
36409
|
if (opts.screenshot !== void 0) {
|
|
@@ -36482,7 +36617,7 @@ var deployCommand = new Command("deploy").description("Deploy to dev or prod").a
|
|
|
36482
36617
|
force: opts.force,
|
|
36483
36618
|
only: opts.only?.split(",").map((s) => s.trim())
|
|
36484
36619
|
});
|
|
36485
|
-
const res = opts.json ? await doDeploy() : await withSpinner(`Deploying to ${target}
|
|
36620
|
+
const res = opts.json ? await doDeploy() : await withSpinner(`Deploying to ${target}...`, doDeploy, { done: null });
|
|
36486
36621
|
const d = res.data;
|
|
36487
36622
|
const inspectAfter = async () => {
|
|
36488
36623
|
if (!opts.inspect) return;
|
|
@@ -36734,8 +36869,8 @@ memoryCommand.command("delete <topic>").description("Delete a topic").option("--
|
|
|
36734
36869
|
// src/commands/sandbox.ts
|
|
36735
36870
|
init_api();
|
|
36736
36871
|
init_config();
|
|
36737
|
-
import { readFileSync as
|
|
36738
|
-
import { dirname as
|
|
36872
|
+
import { readFileSync as readFileSync15, existsSync as existsSync11, statSync as statSync7, mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
|
|
36873
|
+
import { dirname as dirname8, extname as extname4, relative as relative4, resolve as resolve11, sep as sep3 } from "path";
|
|
36739
36874
|
init_colors();
|
|
36740
36875
|
var LANG_MAP = {
|
|
36741
36876
|
js: "javascript",
|
|
@@ -36873,7 +37008,7 @@ function isWriteTarget(code, p) {
|
|
|
36873
37008
|
function resolveRelativeCwd() {
|
|
36874
37009
|
const configPath = getConfigPath();
|
|
36875
37010
|
if (!configPath) return void 0;
|
|
36876
|
-
const projectRoot =
|
|
37011
|
+
const projectRoot = dirname8(configPath);
|
|
36877
37012
|
const rel2 = relative4(projectRoot, process.cwd());
|
|
36878
37013
|
if (!rel2 || rel2.startsWith("..")) return void 0;
|
|
36879
37014
|
return rel2.split(/[\\/]/).filter(Boolean).join("/");
|
|
@@ -36955,7 +37090,7 @@ GCC/Rust).
|
|
|
36955
37090
|
if (args.length >= 2 && INTERPRETERS[args[0].toLowerCase()] !== void 0) {
|
|
36956
37091
|
langFromInterp = INTERPRETERS[args[0].toLowerCase()];
|
|
36957
37092
|
const rest = args.slice(1).join(" ");
|
|
36958
|
-
if (existsSync11(rest) &&
|
|
37093
|
+
if (existsSync11(rest) && statSync7(rest).isFile()) filePath = rest;
|
|
36959
37094
|
else inlineCode = rest;
|
|
36960
37095
|
} else if (args.length === 1) {
|
|
36961
37096
|
inlineCode = args[0];
|
|
@@ -36976,7 +37111,7 @@ GCC/Rust).
|
|
|
36976
37111
|
let source = inlineCode;
|
|
36977
37112
|
if (filePath) {
|
|
36978
37113
|
try {
|
|
36979
|
-
source =
|
|
37114
|
+
source = readFileSync15(filePath, "utf8");
|
|
36980
37115
|
} catch {
|
|
36981
37116
|
console.error(error(`Cannot read file: ${filePath}`));
|
|
36982
37117
|
process.exit(1);
|
|
@@ -37028,7 +37163,7 @@ GCC/Rust).
|
|
|
37028
37163
|
// wire name for --discard-output.)
|
|
37029
37164
|
noSyncOutput: discardOutput.length ? discardOutput : void 0
|
|
37030
37165
|
});
|
|
37031
|
-
const res = opts.json ? await doRun() : await withSpinner("Running in sandbox
|
|
37166
|
+
const res = opts.json ? await doRun() : await withSpinner("Running in sandbox...", doRun, { done: null });
|
|
37032
37167
|
const pulledLocal = !!(res.data.outputFiles?.length && getConfigPath());
|
|
37033
37168
|
if (pulledLocal) {
|
|
37034
37169
|
await sync({ interactive: false, progress: opts.json ? void 0 : createProgressReporter() });
|
|
@@ -37040,7 +37175,7 @@ GCC/Rust).
|
|
|
37040
37175
|
const rel2 = f.path.replace(/\\/g, "/");
|
|
37041
37176
|
const abs = resolve11(base, rel2);
|
|
37042
37177
|
if (abs !== base && !abs.startsWith(base + sep3)) continue;
|
|
37043
|
-
mkdirSync8(
|
|
37178
|
+
mkdirSync8(dirname8(abs), { recursive: true });
|
|
37044
37179
|
writeFileSync9(abs, Buffer.from(f.contentBase64, "base64"));
|
|
37045
37180
|
scratchWritten.push(rel2);
|
|
37046
37181
|
}
|
|
@@ -37096,7 +37231,7 @@ Note: ${rootStrays.join(", ")} landed at the project root and will deploy with t
|
|
|
37096
37231
|
const root = getProjectRoot();
|
|
37097
37232
|
const emptyOutputs = pulledLocal && root ? (res.data.outputFiles ?? []).filter((f) => {
|
|
37098
37233
|
try {
|
|
37099
|
-
return
|
|
37234
|
+
return statSync7(resolve11(root, f)).size === 0;
|
|
37100
37235
|
} catch {
|
|
37101
37236
|
return false;
|
|
37102
37237
|
}
|
|
@@ -37128,7 +37263,7 @@ var chatCommand = new Command("chat").description("Send a message to your agent"
|
|
|
37128
37263
|
const endpoint = useExisting ? `/conversations/${config.conversationGuid}/messages` : "/conversations";
|
|
37129
37264
|
const body = useExisting ? { content: message, projectGuid: config.projectGuid } : { agentGuid: config.agentGuid, content: message, projectGuid: config.projectGuid };
|
|
37130
37265
|
const doChat = () => post(endpoint, body);
|
|
37131
|
-
const res = opts.json ? await doChat() : await withSpinner("Thinking
|
|
37266
|
+
const res = opts.json ? await doChat() : await withSpinner("Thinking...", doChat, { done: null });
|
|
37132
37267
|
if (!oneOff && res.data.conversationGuid !== config.conversationGuid) {
|
|
37133
37268
|
saveConfig({ ...config, conversationGuid: res.data.conversationGuid });
|
|
37134
37269
|
}
|
|
@@ -37213,7 +37348,7 @@ chatCommand.command("delete <guid>").description("Delete a chat").option("--json
|
|
|
37213
37348
|
// src/commands/project.ts
|
|
37214
37349
|
init_api();
|
|
37215
37350
|
init_config();
|
|
37216
|
-
import { join as
|
|
37351
|
+
import { join as join13 } from "path";
|
|
37217
37352
|
import { mkdirSync as mkdirSync9 } from "fs";
|
|
37218
37353
|
init_colors();
|
|
37219
37354
|
init_utils();
|
|
@@ -37271,7 +37406,7 @@ projectCommand.command("create <name>").description("Create a project").option("
|
|
|
37271
37406
|
}
|
|
37272
37407
|
const res = await post("/projects", body);
|
|
37273
37408
|
const project = res.data;
|
|
37274
|
-
const dir =
|
|
37409
|
+
const dir = join13(getProjectsRoot(), project.slug);
|
|
37275
37410
|
mkdirSync9(dir, { recursive: true });
|
|
37276
37411
|
const accountSlug = await getAccountSlug();
|
|
37277
37412
|
let agentGuid = "";
|
|
@@ -37614,7 +37749,7 @@ workflowCommand.command("run <name>").description("Trigger a workflow (add --wai
|
|
|
37614
37749
|
}
|
|
37615
37750
|
if (r.status !== "completed") process.exit(1);
|
|
37616
37751
|
}));
|
|
37617
|
-
workflowCommand.command("runs <name> [runGuid]").description("List recent runs, or pass a run guid (wr_
|
|
37752
|
+
workflowCommand.command("runs <name> [runGuid]").description("List recent runs, or pass a run guid (wr_...) to see that run's per-step outputs").option("--json", "Output as JSON").action((name, runGuid, _opts, cmd) => run("Runs", async () => {
|
|
37618
37753
|
const opts = mergedOpts(cmd);
|
|
37619
37754
|
const wf2 = await resolveWorkflow(name);
|
|
37620
37755
|
if (runGuid) {
|
|
@@ -38100,7 +38235,7 @@ function printUsage(d) {
|
|
|
38100
38235
|
for (const p of d.projects) {
|
|
38101
38236
|
const name = p.projectName ?? "(no project)";
|
|
38102
38237
|
row(
|
|
38103
|
-
name.length > 16 ? `${name.slice(0,
|
|
38238
|
+
name.length > 16 ? `${name.slice(0, 13)}...` : name,
|
|
38104
38239
|
formatBytes(p.liveBytes),
|
|
38105
38240
|
`${p.liveFiles.toLocaleString()} files`
|
|
38106
38241
|
);
|
|
@@ -38109,7 +38244,7 @@ function printUsage(d) {
|
|
|
38109
38244
|
const shownBytes = d.projects.reduce((n, p) => n + p.liveBytes, 0);
|
|
38110
38245
|
const shownFiles = d.projects.reduce((n, p) => n + p.liveFiles, 0);
|
|
38111
38246
|
row(
|
|
38112
|
-
|
|
38247
|
+
`...and ${hidden.toLocaleString()} more`,
|
|
38113
38248
|
formatBytes(totals.liveBytes - shownBytes),
|
|
38114
38249
|
`${(totals.liveFiles - shownFiles).toLocaleString()} files`
|
|
38115
38250
|
);
|
|
@@ -38161,50 +38296,11 @@ storageCommand.command("retention").description("View or adjust your file versio
|
|
|
38161
38296
|
init_platform();
|
|
38162
38297
|
init_auth();
|
|
38163
38298
|
init_api();
|
|
38164
|
-
import { join as
|
|
38165
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync12, readFileSync as readFileSync18, writeFileSync as writeFileSync12, renameSync as renameSync2, readdirSync as
|
|
38299
|
+
import { join as join17, dirname as dirname10, resolve as resolve13, basename as basename5, sep as sep5 } from "path";
|
|
38300
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync12, readFileSync as readFileSync18, writeFileSync as writeFileSync12, renameSync as renameSync2, readdirSync as readdirSync8, statSync as statSync8 } from "fs";
|
|
38166
38301
|
import { homedir as homedir12 } from "os";
|
|
38167
38302
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
38168
38303
|
import { randomUUID } from "crypto";
|
|
38169
|
-
|
|
38170
|
-
// src/login-flow.ts
|
|
38171
|
-
init_api();
|
|
38172
|
-
init_auth();
|
|
38173
|
-
init_utils();
|
|
38174
|
-
init_colors();
|
|
38175
|
-
async function interactiveLogin() {
|
|
38176
|
-
const email = await prompt(" Email: ");
|
|
38177
|
-
if (!email) {
|
|
38178
|
-
console.error(`
|
|
38179
|
-
${error("Email required.")}`);
|
|
38180
|
-
process.exit(1);
|
|
38181
|
-
}
|
|
38182
|
-
await publicPost("/auth/login", { email });
|
|
38183
|
-
console.log(" Check your email for a 6-digit code.\n");
|
|
38184
|
-
const code = await prompt(" Code: ");
|
|
38185
|
-
if (!code) {
|
|
38186
|
-
console.error(`
|
|
38187
|
-
${error("Code required.")}`);
|
|
38188
|
-
process.exit(1);
|
|
38189
|
-
}
|
|
38190
|
-
const res = await publicPost("/auth/verify", { email, code });
|
|
38191
|
-
const exp = decodeJwtExp(res.accessToken);
|
|
38192
|
-
if (!exp) {
|
|
38193
|
-
console.error(`
|
|
38194
|
-
${error("Invalid token received.")}`);
|
|
38195
|
-
process.exit(1);
|
|
38196
|
-
}
|
|
38197
|
-
const expiresAt = new Date(exp * 1e3).toISOString();
|
|
38198
|
-
saveAuth({ accessToken: res.accessToken, refreshToken: res.refreshToken, email, expiresAt });
|
|
38199
|
-
console.log(` ${success(`Logged in (${email}).`)}`);
|
|
38200
|
-
const delivered = await flushBugQueue().catch(() => 0);
|
|
38201
|
-
if (delivered > 0) {
|
|
38202
|
-
console.log(` ${muted(`Delivered ${delivered} queued bug report${delivered === 1 ? "" : "s"}.`)}`);
|
|
38203
|
-
}
|
|
38204
|
-
return getAuth();
|
|
38205
|
-
}
|
|
38206
|
-
|
|
38207
|
-
// src/commands/build.ts
|
|
38208
38304
|
init_config();
|
|
38209
38305
|
|
|
38210
38306
|
// src/prompts.ts
|
|
@@ -38310,11 +38406,10 @@ function buildFreshWrap(contextBlock, userMsg) {
|
|
|
38310
38406
|
// src/relay/onboarding.ts
|
|
38311
38407
|
init_utils();
|
|
38312
38408
|
init_colors();
|
|
38313
|
-
import { readFileSync as readFileSync16 } from "node:fs";
|
|
38314
38409
|
|
|
38315
38410
|
// src/relay/installers.ts
|
|
38316
38411
|
import { homedir as homedir8, platform as osPlatform } from "os";
|
|
38317
|
-
import { join as
|
|
38412
|
+
import { join as join14 } from "path";
|
|
38318
38413
|
var UnsupportedPlatformError = class extends Error {
|
|
38319
38414
|
constructor(plat) {
|
|
38320
38415
|
super(`gipity relay install does not support ${plat}`);
|
|
@@ -38332,8 +38427,8 @@ function display(argvs) {
|
|
|
38332
38427
|
}
|
|
38333
38428
|
function launchdPlan(cliPath) {
|
|
38334
38429
|
const label2 = "ai.gipity.relay";
|
|
38335
|
-
const path5 =
|
|
38336
|
-
const logDir =
|
|
38430
|
+
const path5 = join14(homedir8(), "Library", "LaunchAgents", `${label2}.plist`);
|
|
38431
|
+
const logDir = join14(homedir8(), "Library", "Logs", "Gipity");
|
|
38337
38432
|
const uid = process.getuid?.() ?? 0;
|
|
38338
38433
|
const target = `gui/${uid}`;
|
|
38339
38434
|
const content = `<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -38357,8 +38452,8 @@ function launchdPlan(cliPath) {
|
|
|
38357
38452
|
<dict>
|
|
38358
38453
|
<key>SuccessfulExit</key><false/>
|
|
38359
38454
|
</dict>
|
|
38360
|
-
<key>StandardOutPath</key><string>${
|
|
38361
|
-
<key>StandardErrorPath</key><string>${
|
|
38455
|
+
<key>StandardOutPath</key><string>${join14(logDir, "relay.out.log")}</string>
|
|
38456
|
+
<key>StandardErrorPath</key><string>${join14(logDir, "relay.err.log")}</string>
|
|
38362
38457
|
<key>ProcessType</key><string>Background</string>
|
|
38363
38458
|
</dict>
|
|
38364
38459
|
</plist>
|
|
@@ -38381,7 +38476,7 @@ function launchdPlan(cliPath) {
|
|
|
38381
38476
|
}
|
|
38382
38477
|
function systemdUserPlan(cliPath) {
|
|
38383
38478
|
const unitName = "gipity-relay.service";
|
|
38384
|
-
const path5 =
|
|
38479
|
+
const path5 = join14(homedir8(), ".config", "systemd", "user", unitName);
|
|
38385
38480
|
const content = `[Unit]
|
|
38386
38481
|
Description=Gipity relay - local Claude Code control from the Gipity web CLI
|
|
38387
38482
|
After=network-online.target
|
|
@@ -38419,7 +38514,7 @@ WantedBy=default.target
|
|
|
38419
38514
|
}
|
|
38420
38515
|
function windowsTaskPlan(cliPath) {
|
|
38421
38516
|
const taskName = "GipityRelay";
|
|
38422
|
-
const path5 =
|
|
38517
|
+
const path5 = join14(homedir8(), "AppData", "Local", "Gipity", "relay-task.xml");
|
|
38423
38518
|
const runsViaNode = /\.[cm]?js$/i.test(cliPath);
|
|
38424
38519
|
const command = runsViaNode ? process.execPath : cliPath;
|
|
38425
38520
|
const argLine = runsViaNode ? `"${cliPath}" relay run` : "relay run";
|
|
@@ -38481,13 +38576,13 @@ function windowsTaskPlan(cliPath) {
|
|
|
38481
38576
|
init_platform();
|
|
38482
38577
|
init_api();
|
|
38483
38578
|
import { hostname as hostname3, userInfo, platform as osPlatform2 } from "os";
|
|
38484
|
-
import { resolve as resolve12, dirname as
|
|
38579
|
+
import { resolve as resolve12, dirname as dirname9 } from "path";
|
|
38485
38580
|
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
38486
38581
|
|
|
38487
38582
|
// src/relay/machine-id.ts
|
|
38488
38583
|
import { createHash as createHash3 } from "crypto";
|
|
38489
38584
|
import { execFileSync } from "child_process";
|
|
38490
|
-
import { readFileSync as
|
|
38585
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
38491
38586
|
import { hostname as hostname2, platform, arch } from "os";
|
|
38492
38587
|
function rawMachineId() {
|
|
38493
38588
|
const override = process.env.GIPITY_RELAY_MACHINE_ID?.trim();
|
|
@@ -38496,7 +38591,7 @@ function rawMachineId() {
|
|
|
38496
38591
|
if (platform() === "linux") {
|
|
38497
38592
|
for (const path5 of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
|
|
38498
38593
|
try {
|
|
38499
|
-
const id2 =
|
|
38594
|
+
const id2 = readFileSync16(path5, "utf-8").trim();
|
|
38500
38595
|
if (id2) return `linux:${id2}`;
|
|
38501
38596
|
} catch {
|
|
38502
38597
|
}
|
|
@@ -38591,7 +38686,7 @@ function startDaemon() {
|
|
|
38591
38686
|
function installAutostart(opts = {}) {
|
|
38592
38687
|
const stdio = opts.stdio ?? "ignore";
|
|
38593
38688
|
const plan2 = planFor({ cliPath: resolveCliPath() });
|
|
38594
|
-
mkdirSync10(
|
|
38689
|
+
mkdirSync10(dirname9(plan2.path), { recursive: true });
|
|
38595
38690
|
writeFileSync10(plan2.path, plan2.content);
|
|
38596
38691
|
let ok = true;
|
|
38597
38692
|
for (const argv2 of plan2.enableCmds) {
|
|
@@ -38616,15 +38711,6 @@ function removeAutostart(opts = {}) {
|
|
|
38616
38711
|
|
|
38617
38712
|
// src/relay/onboarding.ts
|
|
38618
38713
|
var ensureDaemonRunning = startDaemon;
|
|
38619
|
-
function isWsl() {
|
|
38620
|
-
if (process.platform !== "linux") return false;
|
|
38621
|
-
if (process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP) return true;
|
|
38622
|
-
try {
|
|
38623
|
-
return /microsoft/i.test(readFileSync16("/proc/version", "utf-8"));
|
|
38624
|
-
} catch {
|
|
38625
|
-
return false;
|
|
38626
|
-
}
|
|
38627
|
-
}
|
|
38628
38714
|
async function runRelaySetup(opts) {
|
|
38629
38715
|
const alreadyAnswered = getRelayEnabled() !== void 0;
|
|
38630
38716
|
if (opts.mode === "offer-once" && alreadyAnswered) {
|
|
@@ -38640,12 +38726,12 @@ async function runRelaySetup(opts) {
|
|
|
38640
38726
|
console.log("");
|
|
38641
38727
|
console.log(` ${success("Registered as")} ${bold(existingDevice.name)} ${muted(`(${existingDevice.guid})`)}`);
|
|
38642
38728
|
if (isPaused()) {
|
|
38643
|
-
console.log(` ${dim("The relay is paused
|
|
38729
|
+
console.log(` ${dim("The relay is paused - resume it with")} ${brand("gipity relay resume")}${dim(".")}`);
|
|
38644
38730
|
} else {
|
|
38645
38731
|
console.log(` ${dim("The relay is running. Open")} ${brand("gipity.ai")}${dim(" and type `/claude` to drive Claude Code here.")}`);
|
|
38646
38732
|
}
|
|
38647
38733
|
console.log("");
|
|
38648
|
-
console.log(` ${dim("To register this computer again
|
|
38734
|
+
console.log(` ${dim("To register this computer again (for example under a different name),")}`);
|
|
38649
38735
|
console.log(` ${dim("unregister it first, then re-run setup:")}`);
|
|
38650
38736
|
console.log(` ${brand("gipity relay revoke")} ${dim("# unpairs this computer and removes the login service")}`);
|
|
38651
38737
|
console.log(` ${brand("gipity connect")} ${dim("# register it again (asks for a new name)")}`);
|
|
@@ -38655,28 +38741,29 @@ async function runRelaySetup(opts) {
|
|
|
38655
38741
|
}
|
|
38656
38742
|
if (opts.mode === "run-now") {
|
|
38657
38743
|
console.log(` ${bold("Set up this computer as a relay")}`);
|
|
38658
|
-
console.log(` ${dim("
|
|
38659
|
-
console.log(` ${dim("
|
|
38744
|
+
console.log(` ${dim("Gipity drives the coding agents on this computer from the web (")}${brand("gipity.ai")}${dim(")")}`);
|
|
38745
|
+
console.log(` ${dim("on any browser, desktop or phone. Uses your Claude, Codex, or Grok")}`);
|
|
38746
|
+
console.log(` ${dim("subscription - the cheapest way to pay for tokens.")}`);
|
|
38660
38747
|
} else {
|
|
38661
|
-
console.log(` ${bold("
|
|
38662
|
-
console.log(` ${dim("
|
|
38663
|
-
console.log("");
|
|
38664
|
-
console.log(` ${dim("Enable now (takes 2 seconds) or turn on later with")} ${brand("gipity connect")}`);
|
|
38748
|
+
console.log(` ${bold("Optional:")} ${dim("Gipity can drive the coding agents on this computer from the")}`);
|
|
38749
|
+
console.log(` ${dim("web (")}${brand("gipity.ai")}${dim(") on any browser, desktop or phone. Like Claude Code remote")}`);
|
|
38750
|
+
console.log(` ${dim("control, but one pane for all your coding agents.")}`);
|
|
38665
38751
|
}
|
|
38666
38752
|
console.log("");
|
|
38667
|
-
const promptText = opts.mode === "run-now" ? " Set up remote control on this computer
|
|
38753
|
+
const promptText = opts.mode === "run-now" ? " Set up remote control on this computer" : " Enable remote control";
|
|
38668
38754
|
const enable = await confirm(promptText, { default: "yes" });
|
|
38669
38755
|
if (!enable) {
|
|
38670
38756
|
setRelayEnabled(false);
|
|
38671
|
-
console.log(` ${muted("Skipped.")}`);
|
|
38757
|
+
console.log(` ${muted("Skipped. Turn on later with")} ${brand("gipity connect")}${muted(".")}`);
|
|
38672
38758
|
console.log("");
|
|
38673
38759
|
return false;
|
|
38674
38760
|
}
|
|
38761
|
+
console.log("");
|
|
38675
38762
|
const defaultName = friendlyDeviceName();
|
|
38676
38763
|
const rawName = await prompt(` Device name [${bold(defaultName)}]: `);
|
|
38677
38764
|
const name = (rawName || defaultName).trim();
|
|
38678
38765
|
if (!name || name.length > 100) {
|
|
38679
|
-
console.error(` ${error("Device name must be 1
|
|
38766
|
+
console.error(` ${error("Device name must be 1-100 non-whitespace characters. Skipping.")}`);
|
|
38680
38767
|
setRelayEnabled(false);
|
|
38681
38768
|
return false;
|
|
38682
38769
|
}
|
|
@@ -38689,11 +38776,9 @@ async function runRelaySetup(opts) {
|
|
|
38689
38776
|
console.error(` ${dim("Skipping for now - we'll offer again next time. Or turn it on with `gipity connect`.")}`);
|
|
38690
38777
|
return false;
|
|
38691
38778
|
}
|
|
38692
|
-
|
|
38693
|
-
|
|
38694
|
-
|
|
38695
|
-
}
|
|
38696
|
-
const autostartOs = await confirm(" Also start at OS login (auto-start with Windows / macOS / Linux)?", { default: "yes" });
|
|
38779
|
+
startDaemon();
|
|
38780
|
+
console.log("");
|
|
38781
|
+
const autostartOs = await confirm(" Also start at OS login", { default: "yes" });
|
|
38697
38782
|
if (autostartOs) {
|
|
38698
38783
|
try {
|
|
38699
38784
|
const res = installAutostart();
|
|
@@ -38705,8 +38790,6 @@ async function runRelaySetup(opts) {
|
|
|
38705
38790
|
} else {
|
|
38706
38791
|
console.log(` ${muted("Autostart install returned non-zero - you can run")} ${brand("gipity relay install")} ${muted("later.")}`);
|
|
38707
38792
|
}
|
|
38708
|
-
} else {
|
|
38709
|
-
console.log(` ${success("Auto-start installed.")} ${dim(res.summary)}`);
|
|
38710
38793
|
}
|
|
38711
38794
|
} catch (err) {
|
|
38712
38795
|
if (err instanceof UnsupportedPlatformError) {
|
|
@@ -38717,16 +38800,12 @@ async function runRelaySetup(opts) {
|
|
|
38717
38800
|
}
|
|
38718
38801
|
}
|
|
38719
38802
|
if (getDiagnosticsConsent() === void 0) {
|
|
38720
|
-
|
|
38721
|
-
|
|
38722
|
-
{ default: "yes" }
|
|
38723
|
-
);
|
|
38803
|
+
console.log("");
|
|
38804
|
+
const diag = await confirm(" Share anonymous diagnostics info", { default: "yes" });
|
|
38724
38805
|
setDiagnosticsConsent(diag);
|
|
38725
|
-
console.log(` ${dim(diag ? "Thanks - no personal data or file paths are ever sent. Turn off anytime with `gipity relay diagnostics off`." : "Diagnostics off. Turn on anytime with `gipity relay diagnostics on`.")}`);
|
|
38726
38806
|
}
|
|
38727
38807
|
console.log("");
|
|
38728
|
-
console.log(` ${success(`
|
|
38729
|
-
console.log(` ${dim("In the Gipity web CLI, type `/claude` to dispatch messages to this PC.")}`);
|
|
38808
|
+
console.log(` ${success(`Done! ${bold(device.name)} is set up. New chats from gipity.ai can now execute here.`)}`);
|
|
38730
38809
|
console.log("");
|
|
38731
38810
|
return true;
|
|
38732
38811
|
}
|
|
@@ -38869,9 +38948,8 @@ function padR(s, width) {
|
|
|
38869
38948
|
}
|
|
38870
38949
|
function leadingEllipsify(s, maxW) {
|
|
38871
38950
|
if (s.length <= maxW) return s;
|
|
38872
|
-
if (maxW <=
|
|
38873
|
-
|
|
38874
|
-
return "\u2026" + s.slice(-(maxW - 1));
|
|
38951
|
+
if (maxW <= 4) return s.slice(-maxW);
|
|
38952
|
+
return "..." + s.slice(-(maxW - 3));
|
|
38875
38953
|
}
|
|
38876
38954
|
function center(s, width) {
|
|
38877
38955
|
const gap = width - visLen(s);
|
|
@@ -38993,7 +39071,7 @@ function printFull(opts, outerW) {
|
|
|
38993
39071
|
import { execSync as execSync2 } from "child_process";
|
|
38994
39072
|
import { existsSync as existsSync12 } from "fs";
|
|
38995
39073
|
import { homedir as homedir10 } from "os";
|
|
38996
|
-
import { join as
|
|
39074
|
+
import { join as join15 } from "path";
|
|
38997
39075
|
var CLAUDE_PACKAGE = "@anthropic-ai/claude-code";
|
|
38998
39076
|
function claudeInstallPlan(platformOverride) {
|
|
38999
39077
|
const plat = platformOverride ?? process.platform;
|
|
@@ -39012,7 +39090,7 @@ function isClaudeInstalled(platformOverride) {
|
|
|
39012
39090
|
}
|
|
39013
39091
|
function isClaudeAuthenticated() {
|
|
39014
39092
|
if (process.env.CLAUDE_CODE_OAUTH_TOKEN || process.env.ANTHROPIC_API_KEY) return true;
|
|
39015
|
-
return existsSync12(
|
|
39093
|
+
return existsSync12(join15(homedir10(), ".claude", ".credentials.json"));
|
|
39016
39094
|
}
|
|
39017
39095
|
function probeClaudeAuthenticated() {
|
|
39018
39096
|
if (!isClaudeInstalled()) return false;
|
|
@@ -39183,8 +39261,8 @@ function getAdapterBySource(source) {
|
|
|
39183
39261
|
// src/prefs.ts
|
|
39184
39262
|
import { existsSync as existsSync13, mkdirSync as mkdirSync11, readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "fs";
|
|
39185
39263
|
import { homedir as homedir11 } from "os";
|
|
39186
|
-
import { join as
|
|
39187
|
-
var PREFS_PATH =
|
|
39264
|
+
import { join as join16 } from "path";
|
|
39265
|
+
var PREFS_PATH = join16(homedir11(), ".gipity", "prefs.json");
|
|
39188
39266
|
function readPrefs() {
|
|
39189
39267
|
if (!existsSync13(PREFS_PATH)) return {};
|
|
39190
39268
|
try {
|
|
@@ -39200,17 +39278,17 @@ function writePrefs(update) {
|
|
|
39200
39278
|
try {
|
|
39201
39279
|
const current = readPrefs();
|
|
39202
39280
|
const next = { ...current, ...update };
|
|
39203
|
-
mkdirSync11(
|
|
39281
|
+
mkdirSync11(join16(homedir11(), ".gipity"), { recursive: true });
|
|
39204
39282
|
writeFileSync11(PREFS_PATH, JSON.stringify(next, null, 2) + "\n");
|
|
39205
39283
|
} catch {
|
|
39206
39284
|
}
|
|
39207
39285
|
}
|
|
39208
39286
|
|
|
39209
39287
|
// src/commands/build.ts
|
|
39210
|
-
var __clDir =
|
|
39288
|
+
var __clDir = dirname10(fileURLToPath2(import.meta.url));
|
|
39211
39289
|
function readOwnPkg(fromDir) {
|
|
39212
|
-
for (let d = fromDir; ; d =
|
|
39213
|
-
const p =
|
|
39290
|
+
for (let d = fromDir; ; d = dirname10(d)) {
|
|
39291
|
+
const p = join17(d, "package.json");
|
|
39214
39292
|
if (existsSync14(p)) {
|
|
39215
39293
|
try {
|
|
39216
39294
|
const pkg2 = JSON.parse(readFileSync18(p, "utf-8"));
|
|
@@ -39218,13 +39296,13 @@ function readOwnPkg(fromDir) {
|
|
|
39218
39296
|
} catch {
|
|
39219
39297
|
}
|
|
39220
39298
|
}
|
|
39221
|
-
if (
|
|
39299
|
+
if (dirname10(d) === d) return {};
|
|
39222
39300
|
}
|
|
39223
39301
|
}
|
|
39224
39302
|
var __clPkg = readOwnPkg(__clDir);
|
|
39225
39303
|
function reportSyncResult(result) {
|
|
39226
39304
|
if (result.aborted) {
|
|
39227
|
-
console.log(` ${warning("Merge cancelled
|
|
39305
|
+
console.log(` ${warning("Merge cancelled - this folder was NOT synced with the project.")}`);
|
|
39228
39306
|
console.log(` ${muted("For a clean copy, quit and open the project in an empty folder. To merge anyway, run `gipity sync`.")}`);
|
|
39229
39307
|
return;
|
|
39230
39308
|
}
|
|
@@ -39232,9 +39310,9 @@ function reportSyncResult(result) {
|
|
|
39232
39310
|
console.log(` Synced ${result.applied} change${result.applied > 1 ? "s" : ""} with Gipity.`);
|
|
39233
39311
|
}
|
|
39234
39312
|
if (result.errors.length) {
|
|
39235
|
-
console.log(` ${warning(`Sync finished with ${result.errors.length} problem${result.errors.length === 1 ? "" : "s"}
|
|
39313
|
+
console.log(` ${warning(`Sync finished with ${result.errors.length} problem${result.errors.length === 1 ? "" : "s"} - your local copy may be incomplete:`)}`);
|
|
39236
39314
|
for (const e of result.errors.slice(0, 8)) console.log(` - ${e}`);
|
|
39237
|
-
if (result.errors.length > 8) console.log(`
|
|
39315
|
+
if (result.errors.length > 8) console.log(` ...and ${result.errors.length - 8} more.`);
|
|
39238
39316
|
console.log(` ${muted("Nothing was deleted. Re-run `gipity sync` to finish the pull.")}`);
|
|
39239
39317
|
}
|
|
39240
39318
|
}
|
|
@@ -39262,12 +39340,12 @@ function localFsFallback(dir) {
|
|
|
39262
39340
|
const topLevelEntries = [];
|
|
39263
39341
|
const walk = (d, depth) => {
|
|
39264
39342
|
try {
|
|
39265
|
-
for (const name of
|
|
39343
|
+
for (const name of readdirSync8(d).sort()) {
|
|
39266
39344
|
if (isSyncIgnored(name)) continue;
|
|
39267
39345
|
let isDir = false;
|
|
39268
39346
|
let size = 0;
|
|
39269
39347
|
try {
|
|
39270
|
-
const st2 =
|
|
39348
|
+
const st2 = statSync8(join17(d, name));
|
|
39271
39349
|
isDir = st2.isDirectory();
|
|
39272
39350
|
size = st2.isFile() ? st2.size : 0;
|
|
39273
39351
|
} catch {
|
|
@@ -39276,7 +39354,7 @@ function localFsFallback(dir) {
|
|
|
39276
39354
|
if (isDir) {
|
|
39277
39355
|
folderCount++;
|
|
39278
39356
|
if (depth === 0) topLevelEntries.push(`${name}/`);
|
|
39279
|
-
walk(
|
|
39357
|
+
walk(join17(d, name), depth + 1);
|
|
39280
39358
|
} else {
|
|
39281
39359
|
fileCount++;
|
|
39282
39360
|
totalBytes += size;
|
|
@@ -39287,7 +39365,7 @@ function localFsFallback(dir) {
|
|
|
39287
39365
|
}
|
|
39288
39366
|
};
|
|
39289
39367
|
walk(dir, 0);
|
|
39290
|
-
const topLevel = topLevelEntries.length ? topLevelEntries.slice(0, 20).join(", ") + (topLevelEntries.length > 20 ? ",
|
|
39368
|
+
const topLevel = topLevelEntries.length ? topLevelEntries.slice(0, 20).join(", ") + (topLevelEntries.length > 20 ? ", ..." : "") : "(empty directory)";
|
|
39291
39369
|
return { fileCount, folderCount, totalBytes, topLevel };
|
|
39292
39370
|
}
|
|
39293
39371
|
async function buildProjectContextBlock2(opts) {
|
|
@@ -39309,7 +39387,7 @@ function hasStreamJsonFlag(args) {
|
|
|
39309
39387
|
}
|
|
39310
39388
|
function markFolderTrusted(dir) {
|
|
39311
39389
|
try {
|
|
39312
|
-
const file =
|
|
39390
|
+
const file = join17(homedir12(), ".claude.json");
|
|
39313
39391
|
const cfg = existsSync14(file) ? JSON.parse(readFileSync18(file, "utf-8")) : {};
|
|
39314
39392
|
if (typeof cfg !== "object" || cfg === null) return;
|
|
39315
39393
|
if (typeof cfg.projects !== "object" || cfg.projects === null) cfg.projects = {};
|
|
@@ -39437,7 +39515,7 @@ async function runLaunch(cmdName, cmdCfg, opts) {
|
|
|
39437
39515
|
if (existing && !opts.newProject && !opts.project && !nonInteractive) {
|
|
39438
39516
|
const cwdHasConfig = existsSync14(resolve13(process.cwd(), ".gipity.json"));
|
|
39439
39517
|
if (!cwdHasConfig && isLikelyEmpty(process.cwd())) {
|
|
39440
|
-
const ancestorRoot =
|
|
39518
|
+
const ancestorRoot = dirname10(getConfigPath());
|
|
39441
39519
|
console.log(` ${bold("You are inside an existing Gipity project.")}
|
|
39442
39520
|
`);
|
|
39443
39521
|
console.log(` ${bold("1.")} Continue in ${brand(existing.projectSlug)} ${muted(`(rooted at ${formatCwdLabel(ancestorRoot)})`)}`);
|
|
@@ -39484,7 +39562,7 @@ async function runLaunch(cmdName, cmdCfg, opts) {
|
|
|
39484
39562
|
}
|
|
39485
39563
|
project = found;
|
|
39486
39564
|
}
|
|
39487
|
-
const projectDir2 = opts.here ? process.cwd() :
|
|
39565
|
+
const projectDir2 = opts.here ? process.cwd() : join17(getProjectsRoot(), project.slug);
|
|
39488
39566
|
mkdirSync12(projectDir2, { recursive: true });
|
|
39489
39567
|
process.chdir(projectDir2);
|
|
39490
39568
|
clearConfigCache();
|
|
@@ -39513,6 +39591,7 @@ async function runLaunch(cmdName, cmdCfg, opts) {
|
|
|
39513
39591
|
console.log(` ${warning("Could not sync files (will retry on next prompt):")} ${err.message}`);
|
|
39514
39592
|
}
|
|
39515
39593
|
setupProjectTools();
|
|
39594
|
+
if (!nonInteractive) console.log(` ${muted("Installed Gipity plugin, skills, and hooks.")}`);
|
|
39516
39595
|
if (nonInteractive) {
|
|
39517
39596
|
headlessNewProject = isNewProject;
|
|
39518
39597
|
} else {
|
|
@@ -39526,7 +39605,7 @@ async function runLaunch(cmdName, cmdCfg, opts) {
|
|
|
39526
39605
|
`);
|
|
39527
39606
|
setupProjectTools();
|
|
39528
39607
|
let doSync = true;
|
|
39529
|
-
const projectRoot =
|
|
39608
|
+
const projectRoot = dirname10(getConfigPath());
|
|
39530
39609
|
const scan = scanForAdoption(projectRoot);
|
|
39531
39610
|
if (scan.tier === "refuse" && !nonInteractive) {
|
|
39532
39611
|
const sizeStr = scan.truncated ? `>${formatBytes(ADOPT_THRESHOLDS.REFUSE_BYTES)}` : formatBytes(scan.bytes);
|
|
@@ -39585,7 +39664,7 @@ async function runLaunch(cmdName, cmdCfg, opts) {
|
|
|
39585
39664
|
let agentGuid = "";
|
|
39586
39665
|
const result = forceAdoptCwd ? { kind: "adopt-cwd" } : projects.length > 0 ? await pickOrCreateProject(projects, existingSlugs) : { kind: "create-new", project: await createNewProject(existingSlugs) };
|
|
39587
39666
|
if (result.kind === "adopt-cwd") {
|
|
39588
|
-
const cwdBase =
|
|
39667
|
+
const cwdBase = basename5(process.cwd());
|
|
39589
39668
|
const adoptSlug = slugify(cwdBase) || "project";
|
|
39590
39669
|
const adoptName = (cwdBase || adoptSlug).slice(0, 100);
|
|
39591
39670
|
accountSlug = await getAccountSlug();
|
|
@@ -39607,7 +39686,7 @@ async function runLaunch(cmdName, cmdCfg, opts) {
|
|
|
39607
39686
|
} else {
|
|
39608
39687
|
project = result.project;
|
|
39609
39688
|
isNewProject = result.kind === "create-new";
|
|
39610
|
-
const projectDir2 =
|
|
39689
|
+
const projectDir2 = join17(getProjectsRoot(), project.slug);
|
|
39611
39690
|
mkdirSync12(projectDir2, { recursive: true });
|
|
39612
39691
|
process.chdir(projectDir2);
|
|
39613
39692
|
clearConfigCache();
|
|
@@ -39637,6 +39716,7 @@ async function runLaunch(cmdName, cmdCfg, opts) {
|
|
|
39637
39716
|
}
|
|
39638
39717
|
initialPrompt = "";
|
|
39639
39718
|
setupProjectTools();
|
|
39719
|
+
if (!nonInteractive) console.log(` ${muted("Installed Gipity plugin, skills, and hooks.")}`);
|
|
39640
39720
|
console.log(` ${success(`Project "${project.name}" ready.`)}
|
|
39641
39721
|
`);
|
|
39642
39722
|
}
|
|
@@ -39732,9 +39812,8 @@ async function runLaunch(cmdName, cmdCfg, opts) {
|
|
|
39732
39812
|
}
|
|
39733
39813
|
if (!nonInteractive) {
|
|
39734
39814
|
console.log(` ${bold("Launching Claude Code, powered by Gipity.")}`);
|
|
39735
|
-
console.log(` ${muted("Just tell Claude what you'd like to build or do - everything Claude can do,")}`);
|
|
39736
|
-
console.log(` ${muted("plus hosting, databases, and live deploys on Gipity.")}`);
|
|
39737
39815
|
if (convGuidForHooks) {
|
|
39816
|
+
console.log("");
|
|
39738
39817
|
console.log(` ${muted(`This session is saved to Gipity - watch it live at ${brand("prompt.gipity.ai")}. (--no-capture on init to disable.)`)}`);
|
|
39739
39818
|
}
|
|
39740
39819
|
console.log("");
|
|
@@ -39832,10 +39911,8 @@ async function pickAgent(lastUsed) {
|
|
|
39832
39911
|
console.log(` ${bold("Which coding agent?")}
|
|
39833
39912
|
`);
|
|
39834
39913
|
AGENT_ADAPTERS.forEach((a, i) => {
|
|
39835
|
-
const installed = binaryOnPath(a.binary) || a.key === "claude" && isClaudeInstalled();
|
|
39836
39914
|
const notes = [];
|
|
39837
39915
|
if (a.key === lastUsed) notes.push("last used");
|
|
39838
|
-
if (!installed) notes.push(a.ensureInstalled ? "not installed - we'll install it" : "not installed");
|
|
39839
39916
|
if (a.key === "codex" && !a.hooksSupportedOnPlatform(process.platform)) {
|
|
39840
39917
|
notes.push("session recording unavailable on Windows");
|
|
39841
39918
|
}
|
|
@@ -39948,9 +40025,8 @@ Sending to ${adapter.displayName}: ${message}
|
|
|
39948
40025
|
} else {
|
|
39949
40026
|
agentArgs = [...adapter.buildInteractiveArgs({ resume, model }), ...extras];
|
|
39950
40027
|
console.log(` ${bold(`Launching ${adapter.displayName}, powered by Gipity.`)}`);
|
|
39951
|
-
console.log(` ${muted(`Just tell ${adapter.displayName} what you'd like to build or do - plus hosting,`)}`);
|
|
39952
|
-
console.log(` ${muted("databases, and live deploys on Gipity.")}`);
|
|
39953
40028
|
if (convGuid) {
|
|
40029
|
+
console.log("");
|
|
39954
40030
|
console.log(` ${muted(`This session is saved to Gipity - watch it live at ${brand("prompt.gipity.ai")}. (--no-capture on init to disable.)`)}`);
|
|
39955
40031
|
}
|
|
39956
40032
|
if (adapter.oneTimeSetupNote) {
|
|
@@ -39989,7 +40065,7 @@ ${doneLine}
|
|
|
39989
40065
|
});
|
|
39990
40066
|
}
|
|
39991
40067
|
function flushHeadlessCapture(adapter, sessionId, convGuid) {
|
|
39992
|
-
const runner =
|
|
40068
|
+
const runner = join17(__clDir, "..", "hooks", "capture-runner.js");
|
|
39993
40069
|
if (!existsSync14(runner)) return;
|
|
39994
40070
|
const payload = JSON.stringify({ session_id: sessionId, cwd: process.cwd() });
|
|
39995
40071
|
const env = { ...process.env, GIPITY_CONVERSATION_GUID: convGuid };
|
|
@@ -40380,7 +40456,7 @@ var addCommand = new Command("add").description("Add a template or kit").argumen
|
|
|
40380
40456
|
console.error(muted(isKit ? "Installing kit (server-side install pipeline)..." : "Installing (server writes files + generates favicons; first add for a title can take ~10s)..."));
|
|
40381
40457
|
}
|
|
40382
40458
|
const doAdd = () => post(`/projects/${config.projectGuid}/add`, body);
|
|
40383
|
-
const res = opts.json ? await doAdd() : await withSpinner("Installing
|
|
40459
|
+
const res = opts.json ? await doAdd() : await withSpinner("Installing...", doAdd, { done: null });
|
|
40384
40460
|
const syncResult = await sync({ interactive: false, progress: opts.json ? void 0 : createProgressReporter() });
|
|
40385
40461
|
const data = res.data;
|
|
40386
40462
|
if (opts.json) {
|
|
@@ -40428,7 +40504,7 @@ var removeCommand = new Command("remove").description("Remove a kit").argument("
|
|
|
40428
40504
|
}
|
|
40429
40505
|
}
|
|
40430
40506
|
const doRemove = () => post(`/projects/${config.projectGuid}/remove`, { name: kit });
|
|
40431
|
-
const res = opts.json ? await doRemove() : await withSpinner("Removing
|
|
40507
|
+
const res = opts.json ? await doRemove() : await withSpinner("Removing...", doRemove, { done: null });
|
|
40432
40508
|
const data = res.data;
|
|
40433
40509
|
const syncResult = await sync({
|
|
40434
40510
|
interactive: false,
|
|
@@ -40478,7 +40554,7 @@ function dispositionFilename(headers) {
|
|
|
40478
40554
|
var saveCommand = new Command("save").description("Save this app as a portable .gip bundle").argument("[output]", "Output .gip path, or a directory to write <slug>.gip into (default: ./<slug>.gip)").option("--json", "Output as JSON").addHelpText("after", "\nRestore anywhere with `gipity load <file>.gip` - it always creates a NEW project.").action((output, opts) => run("Save", async () => {
|
|
40479
40555
|
const config = requireConfig();
|
|
40480
40556
|
const doExport = () => downloadWithHeaders(`/projects/${config.projectGuid}/export`);
|
|
40481
|
-
const { buffer, headers } = opts.json ? await doExport() : await withSpinner("Exporting
|
|
40557
|
+
const { buffer, headers } = opts.json ? await doExport() : await withSpinner("Exporting...", doExport, { done: null });
|
|
40482
40558
|
const filename = dispositionFilename(headers) ?? `${config.projectSlug}.gip`;
|
|
40483
40559
|
let dest = path2.resolve(output ?? filename);
|
|
40484
40560
|
if (output && fs3.existsSync(dest) && fs3.statSync(dest).isDirectory()) {
|
|
@@ -40486,14 +40562,14 @@ var saveCommand = new Command("save").description("Save this app as a portable .
|
|
|
40486
40562
|
}
|
|
40487
40563
|
fs3.mkdirSync(path2.dirname(dest), { recursive: true });
|
|
40488
40564
|
fs3.writeFileSync(dest, buffer);
|
|
40489
|
-
const
|
|
40565
|
+
const sha256 = createHash4("sha256").update(buffer).digest("hex");
|
|
40490
40566
|
const skipped = Number(headers.get("x-gip-skipped") ?? 0);
|
|
40491
40567
|
const entries = zipEntryCount(buffer);
|
|
40492
40568
|
if (opts.json) {
|
|
40493
40569
|
console.log(JSON.stringify({
|
|
40494
40570
|
path: dest,
|
|
40495
40571
|
bytes: buffer.length,
|
|
40496
|
-
sha256
|
|
40572
|
+
sha256,
|
|
40497
40573
|
...entries !== null ? { entries } : {},
|
|
40498
40574
|
skipped
|
|
40499
40575
|
}));
|
|
@@ -40503,7 +40579,7 @@ var saveCommand = new Command("save").description("Save this app as a portable .
|
|
|
40503
40579
|
console.log(` Path: ${dest}`);
|
|
40504
40580
|
console.log(` Size: ${formatBytes(buffer.length)}`);
|
|
40505
40581
|
if (entries !== null) console.log(` Entries: ${entries} (including the bundle manifest)`);
|
|
40506
|
-
console.log(` SHA-256: ${
|
|
40582
|
+
console.log(` SHA-256: ${sha256}`);
|
|
40507
40583
|
if (skipped > 0) {
|
|
40508
40584
|
console.log("");
|
|
40509
40585
|
console.warn(warning(`${skipped} file(s) were skipped (unreadable or over 100 MB) - this bundle is partial.`));
|
|
@@ -40620,7 +40696,7 @@ var loadCommand = new Command("load").description("Create a new app from a .gip
|
|
|
40620
40696
|
};
|
|
40621
40697
|
}
|
|
40622
40698
|
if (opts.inspect) {
|
|
40623
|
-
const res = opts.json ? await doInspect() : await withSpinner("Inspecting
|
|
40699
|
+
const res = opts.json ? await doInspect() : await withSpinner("Inspecting...", doInspect, { done: null });
|
|
40624
40700
|
if (opts.json) {
|
|
40625
40701
|
console.log(JSON.stringify(res.data));
|
|
40626
40702
|
return;
|
|
@@ -40628,7 +40704,7 @@ var loadCommand = new Command("load").description("Create a new app from a .gip
|
|
|
40628
40704
|
printInspect(res.data, source);
|
|
40629
40705
|
return;
|
|
40630
40706
|
}
|
|
40631
|
-
const { data, partial } = opts.json ? await doImport() : await withSpinner("Importing
|
|
40707
|
+
const { data, partial } = opts.json ? await doImport() : await withSpinner("Importing...", doImport, { done: null });
|
|
40632
40708
|
const project = data.project;
|
|
40633
40709
|
const dir = path3.join(getProjectsRoot(), project.slug);
|
|
40634
40710
|
fs4.mkdirSync(dir, { recursive: true });
|
|
@@ -41029,7 +41105,7 @@ ${error(`\u26A0 ${problems} error/crash line(s) flagged above`)}`
|
|
|
41029
41105
|
);
|
|
41030
41106
|
if (problems > 0) process.exitCode = 1;
|
|
41031
41107
|
}
|
|
41032
|
-
var pageTestCommand = new Command("test").description("Multi-client realtime check: load a URL in N concurrent headless clients; flag console errors, or drive an action and observe shared state (--observe)").argument("<url>", "Deployed URL to load in every client. {{label}}/{{i}} substitute per client in both modes (e.g. ?name=Bot{{i}}, or ?role={{label}} with --labels host,join), so one invocation can give each client a distinct role.").option("--clients <n>", "Number of headless clients to launch", "2").option("--stagger <s>", "Seconds between client starts (passive default 12; interactive default 0)").option("--wait <ms>", "Passive mode: ms each client stays open after load (max 30000)", "24000").option("--observe <expr>", "Interactive: JS expression sampled in each client to read shared state (e.g. presence count). Switches on interactive mode.").option("--action <expr>", "Interactive: one-time JS run in each client before observing (e.g. fill a name + submit). {{label}}/{{i}} are substituted per client.").option("--labels <csv>", "Per-client labels substituted for {{label}} in the URL/--action/--observe (default client-0, client-1,
|
|
41108
|
+
var pageTestCommand = new Command("test").description("Multi-client realtime check: load a URL in N concurrent headless clients; flag console errors, or drive an action and observe shared state (--observe)").argument("<url>", "Deployed URL to load in every client. {{label}}/{{i}} substitute per client in both modes (e.g. ?name=Bot{{i}}, or ?role={{label}} with --labels host,join), so one invocation can give each client a distinct role.").option("--clients <n>", "Number of headless clients to launch", "2").option("--stagger <s>", "Seconds between client starts (passive default 12; interactive default 0)").option("--wait <ms>", "Passive mode: ms each client stays open after load (max 30000)", "24000").option("--observe <expr>", "Interactive: JS expression sampled in each client to read shared state (e.g. presence count). Switches on interactive mode.").option("--action <expr>", "Interactive: one-time JS run in each client before observing (e.g. fill a name + submit). {{label}}/{{i}} are substituted per client.").option("--labels <csv>", "Per-client labels substituted for {{label}} in the URL/--action/--observe (default client-0, client-1, ...)").option("--hold <ms>", `Interactive: total observe window per client (${MIN_HOLD_MS}-${MAX_HOLD_MS}ms)`, "8000").option("--samples <k>", "Interactive: number of observations across the hold window (2-30)", "6").option("--wait-for <selector>", "Interactive: wait for this CSS selector before running --action (deterministic readiness gate)").option("--json", "Output as JSON").addHelpText("after", `
|
|
41033
41109
|
Examples:
|
|
41034
41110
|
# Passive: load in 3 staggered clients, flag console errors
|
|
41035
41111
|
gipity page test "https://dev.gipity.ai/me/app/" --clients 3 --stagger 8
|
|
@@ -41087,9 +41163,6 @@ function looksLikeHtml(body) {
|
|
|
41087
41163
|
const head = body.slice(0, 300).toLowerCase();
|
|
41088
41164
|
return /<!doctype html|<html[\s>]/.test(head);
|
|
41089
41165
|
}
|
|
41090
|
-
function sha256(s) {
|
|
41091
|
-
return createHash5("sha256").update(s).digest("hex");
|
|
41092
|
-
}
|
|
41093
41166
|
function baseWithSlash(url) {
|
|
41094
41167
|
return url.endsWith("/") ? url : url + "/";
|
|
41095
41168
|
}
|
|
@@ -41099,8 +41172,14 @@ function resolveUrl(base, path5) {
|
|
|
41099
41172
|
async function fetchRaw(url) {
|
|
41100
41173
|
try {
|
|
41101
41174
|
const res = await fetch(url, { redirect: "follow" });
|
|
41102
|
-
const
|
|
41103
|
-
return {
|
|
41175
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
41176
|
+
return {
|
|
41177
|
+
status: res.status,
|
|
41178
|
+
contentType: res.headers.get("content-type"),
|
|
41179
|
+
body: buf.toString("utf-8"),
|
|
41180
|
+
bytes: buf.length,
|
|
41181
|
+
sha256: createHash5("sha256").update(buf).digest("hex")
|
|
41182
|
+
};
|
|
41104
41183
|
} catch {
|
|
41105
41184
|
return null;
|
|
41106
41185
|
}
|
|
@@ -41112,17 +41191,16 @@ async function probeShell(base) {
|
|
|
41112
41191
|
return {
|
|
41113
41192
|
served: r.status >= 200 && r.status < 300,
|
|
41114
41193
|
status: r.status,
|
|
41115
|
-
sha256:
|
|
41194
|
+
sha256: r.sha256,
|
|
41116
41195
|
isHtml: looksLikeHtml(r.body)
|
|
41117
41196
|
};
|
|
41118
41197
|
}
|
|
41119
41198
|
function classify(path5, r, shell) {
|
|
41120
41199
|
if (!r) return { status: null, contentType: null, bytes: 0, verdict: "MISSING", detail: "fetch failed (host unreachable)" };
|
|
41121
|
-
const
|
|
41122
|
-
const base = { status: r.status, contentType: r.contentType, bytes };
|
|
41200
|
+
const base = { status: r.status, contentType: r.contentType, bytes: r.bytes };
|
|
41123
41201
|
if (r.status >= 400) return { ...base, verdict: "MISSING", detail: `HTTP ${r.status}` };
|
|
41124
41202
|
const expect = expectedType(path5);
|
|
41125
|
-
if (shell.served && shell.sha256 &&
|
|
41203
|
+
if (shell.served && shell.sha256 && r.sha256 === shell.sha256) {
|
|
41126
41204
|
return { ...base, verdict: "MISSING", detail: `HTTP ${r.status} but served the SPA shell (file not deployed)` };
|
|
41127
41205
|
}
|
|
41128
41206
|
if (expect && expect !== "html" && looksLikeHtml(r.body)) {
|
|
@@ -41138,7 +41216,7 @@ var TAG = {
|
|
|
41138
41216
|
"MISSING": error("MISSING "),
|
|
41139
41217
|
"WRONG-TYPE": warning("WRONG-TYPE")
|
|
41140
41218
|
};
|
|
41141
|
-
var pageFetchCommand = new Command("fetch").description("Verify deployed non-rendered files (llms.txt, AGENTS.md, robots.txt, served JSON
|
|
41219
|
+
var pageFetchCommand = new Command("fetch").description("Verify deployed non-rendered files (llms.txt, AGENTS.md, robots.txt, served JSON...) really exist - catches the static-host trap where a missing file returns 200 with the SPA shell instead of a 404").argument("<url>", "Deployed app base URL; file paths resolve relative to it").argument("<paths...>", "One or more file paths to verify, e.g. llms.txt AGENTS.md robots.txt").option("--json", "Output as JSON").action((url, paths, opts) => run("Page fetch", async () => {
|
|
41142
41220
|
const shell = await probeShell(url);
|
|
41143
41221
|
const results = [];
|
|
41144
41222
|
for (const p of paths) {
|
|
@@ -41383,7 +41461,7 @@ var SERVICES = [
|
|
|
41383
41461
|
{ name: "location/ip", method: "POST", desc: "IP geolocation ({ ip? })" },
|
|
41384
41462
|
{ name: "location/geocode", method: "POST", desc: "Reverse geocode ({ lat, lon })" }
|
|
41385
41463
|
];
|
|
41386
|
-
var serviceCommand = new Command("service").description("Call an app service (llm, tts, image, transcribe, ...)").addHelpText("after", "\nCall a Gipity app service: LLM, image, music, TTS, and more
|
|
41464
|
+
var serviceCommand = new Command("service").description("Call an app service (llm, tts, image, transcribe, ...)").addHelpText("after", "\nCall a Gipity app service: LLM, image, music, TTS, and more.\nPer-service docs: gipity skill read app-llm | app-tts | app-image | app-video | app-audio (transcribe, sound, music) | app-location");
|
|
41387
41465
|
serviceCommand.command("list").description("List callable app services").option("--json", "Output the service list as JSON").action((opts) => run("Services", async () => {
|
|
41388
41466
|
requireConfig();
|
|
41389
41467
|
if (opts.json) {
|
|
@@ -41429,7 +41507,7 @@ secretsCommand.command("list").alias("ls").description("List secret names (never
|
|
|
41429
41507
|
}
|
|
41430
41508
|
console.log(bold(`${res.data.length} ${scope} secret${res.data.length === 1 ? "" : "s"}:`));
|
|
41431
41509
|
for (const s of res.data) {
|
|
41432
|
-
const masked = s.preview ? muted(
|
|
41510
|
+
const masked = s.preview ? muted(`...${s.preview}`) : muted("(hidden)");
|
|
41433
41511
|
console.log(` ${s.name} ${masked} ${muted(`updated ${new Date(s.updated_at).toLocaleDateString()}`)}`);
|
|
41434
41512
|
}
|
|
41435
41513
|
}));
|
|
@@ -41738,7 +41816,11 @@ jobCommand.command("status <runGuid>").description("Show status of a job run").o
|
|
|
41738
41816
|
const r = res.data;
|
|
41739
41817
|
const statusColor = r.status === "success" ? success : r.status === "failed" ? error : muted;
|
|
41740
41818
|
console.log(`${statusColor(r.status)} ${muted(r.guid)}`);
|
|
41741
|
-
if (r.progress_pct != null)
|
|
41819
|
+
if (r.progress_pct != null) {
|
|
41820
|
+
const prog = `${Math.round(r.progress_pct * 100)}%${r.progress_message ? ` (${r.progress_message})` : ""}`;
|
|
41821
|
+
const failedish = r.status === "failed" || r.status === "cancelled" || r.status === "canceled" || r.status === "error";
|
|
41822
|
+
console.log(failedish ? `last progress before ${r.status}: ${prog}` : `progress: ${prog}`);
|
|
41823
|
+
}
|
|
41742
41824
|
if (r.duration_ms != null) console.log(`duration: ${r.duration_ms}ms`);
|
|
41743
41825
|
if (r.error) console.log(`${error("error:")} ${r.error}`);
|
|
41744
41826
|
if (r.output) console.log(`output: ${JSON.stringify(r.output)}`);
|
|
@@ -41758,7 +41840,7 @@ jobCommand.command("wait <runGuid>").description("Block until a job run finishes
|
|
|
41758
41840
|
if (!opts.json) {
|
|
41759
41841
|
const prog = r.progress_pct != null ? `${Math.round(r.progress_pct * 100)}%${r.progress_message ? ` (${r.progress_message})` : ""}` : r.status;
|
|
41760
41842
|
if (prog !== lastProgress) {
|
|
41761
|
-
console.error(muted(
|
|
41843
|
+
console.error(muted(`... ${prog}`));
|
|
41762
41844
|
lastProgress = prog;
|
|
41763
41845
|
}
|
|
41764
41846
|
}
|
|
@@ -41829,7 +41911,7 @@ jobCommand.command("logs <runGuid>").description("Stream live logs for a job run
|
|
|
41829
41911
|
res = await fetch(url, { headers, signal: controller.signal });
|
|
41830
41912
|
} catch (e) {
|
|
41831
41913
|
if (peekedOut) {
|
|
41832
|
-
console.error(muted(
|
|
41914
|
+
console.error(muted(`... still streaming after ${peekSeconds}s. Run \`gipity job logs ${runGuid} --timeout ${Math.round(peekSeconds)}\` again to keep watching, or \`gipity job wait ${runGuid}\` to block until it finishes.`));
|
|
41833
41915
|
return;
|
|
41834
41916
|
}
|
|
41835
41917
|
throw e;
|
|
@@ -41849,7 +41931,7 @@ jobCommand.command("logs <runGuid>").description("Stream live logs for a job run
|
|
|
41849
41931
|
chunk = await reader.read();
|
|
41850
41932
|
} catch (e) {
|
|
41851
41933
|
if (peekedOut) {
|
|
41852
|
-
console.error(muted(
|
|
41934
|
+
console.error(muted(`... still streaming after ${peekSeconds}s. Run \`gipity job logs ${runGuid} --timeout ${Math.round(peekSeconds)}\` again to keep watching, or \`gipity job wait ${runGuid}\` to block until it finishes.`));
|
|
41853
41935
|
return;
|
|
41854
41936
|
}
|
|
41855
41937
|
throw e;
|
|
@@ -41912,11 +41994,11 @@ jobCommand.command("cancel <runGuid>").description("Cancel a queued or running j
|
|
|
41912
41994
|
}
|
|
41913
41995
|
}));
|
|
41914
41996
|
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) => {
|
|
41915
|
-
const { existsSync: existsSync24, statSync:
|
|
41916
|
-
const { resolve: resolve19, join:
|
|
41997
|
+
const { existsSync: existsSync24, statSync: statSync12 } = await import("node:fs");
|
|
41998
|
+
const { resolve: resolve19, join: join27 } = await import("node:path");
|
|
41917
41999
|
const { spawnCommand: spawnCommand2, spawnSyncCommand: spawnSyncCommand2 } = await Promise.resolve().then(() => (init_platform(), platform_exports));
|
|
41918
42000
|
const jobDir = resolve19(process.cwd(), "jobs", name);
|
|
41919
|
-
if (!existsSync24(jobDir) || !
|
|
42001
|
+
if (!existsSync24(jobDir) || !statSync12(jobDir).isDirectory()) {
|
|
41920
42002
|
console.error(error(`jobs/${name}/ not found in current directory`));
|
|
41921
42003
|
process.exit(1);
|
|
41922
42004
|
}
|
|
@@ -41937,7 +42019,7 @@ jobCommand.command("run-local <name>").description("Run the job in a local Docke
|
|
|
41937
42019
|
},
|
|
41938
42020
|
{ file: "main.sh", runtime: "bash", entry: "bash" }
|
|
41939
42021
|
];
|
|
41940
|
-
const picked = variants.find((v7) => existsSync24(
|
|
42022
|
+
const picked = variants.find((v7) => existsSync24(join27(jobDir, v7.file)));
|
|
41941
42023
|
if (!picked) {
|
|
41942
42024
|
console.error(error(`No main.py / main.js / main.sh found under jobs/${name}/`));
|
|
41943
42025
|
process.exit(1);
|
|
@@ -41962,7 +42044,7 @@ jobCommand.command("run-local <name>").description("Run the job in a local Docke
|
|
|
41962
42044
|
"-v",
|
|
41963
42045
|
`${jobDir}:/work`
|
|
41964
42046
|
];
|
|
41965
|
-
const needsDeps = opts.deps !== false && picked.depsFile && picked.installCmd && existsSync24(
|
|
42047
|
+
const needsDeps = opts.deps !== false && picked.depsFile && picked.installCmd && existsSync24(join27(jobDir, picked.depsFile));
|
|
41966
42048
|
const handlerCmd = `${picked.entry} /work/${picked.file}`;
|
|
41967
42049
|
let shellCmd;
|
|
41968
42050
|
if (needsDeps && picked.installCmd) {
|
|
@@ -42138,7 +42220,7 @@ init_api();
|
|
|
42138
42220
|
init_config();
|
|
42139
42221
|
init_colors();
|
|
42140
42222
|
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync13, readFileSync as readFileSync19 } from "fs";
|
|
42141
|
-
import { resolve as resolvePath2, dirname as
|
|
42223
|
+
import { resolve as resolvePath2, dirname as dirname11, relative as relative5, isAbsolute, basename as basename6 } from "path";
|
|
42142
42224
|
|
|
42143
42225
|
// src/provider-docs.ts
|
|
42144
42226
|
var IMAGE_GEMINI_ASPECT_RATIOS = `1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 4:5, 5:4, 21:9`;
|
|
@@ -42199,11 +42281,11 @@ function noteFormatMismatch(filename, buf) {
|
|
|
42199
42281
|
const asked = dot > 0 ? filename.slice(dot + 1).toLowerCase() : "";
|
|
42200
42282
|
if (!asked || canonicalExt(asked) === actual) return;
|
|
42201
42283
|
console.error(muted(
|
|
42202
|
-
`Note: ${
|
|
42284
|
+
`Note: ${basename6(filename)} holds ${formatName(actual)} bytes though its name ends .${asked} \u2014 that's fine (browsers and image/video/edit tools sniff the bytes, not the name). Saved at the exact path you requested; reference it as-is.`
|
|
42203
42285
|
));
|
|
42204
42286
|
}
|
|
42205
42287
|
function ensureOutputDir(output) {
|
|
42206
|
-
const dir =
|
|
42288
|
+
const dir = dirname11(resolvePath2(output));
|
|
42207
42289
|
try {
|
|
42208
42290
|
mkdirSync13(dir, { recursive: true });
|
|
42209
42291
|
} catch (err) {
|
|
@@ -42213,7 +42295,7 @@ function ensureOutputDir(output) {
|
|
|
42213
42295
|
async function pushGenerated(savedPath) {
|
|
42214
42296
|
const configPath = getConfigPath();
|
|
42215
42297
|
if (!configPath) return;
|
|
42216
|
-
const rel2 = relative5(
|
|
42298
|
+
const rel2 = relative5(dirname11(configPath), savedPath);
|
|
42217
42299
|
if (rel2.startsWith("..") || isAbsolute(rel2)) return;
|
|
42218
42300
|
try {
|
|
42219
42301
|
await pushFile(savedPath);
|
|
@@ -42272,7 +42354,7 @@ Examples:
|
|
|
42272
42354
|
seed: Number.isFinite(opts.seed) ? opts.seed : void 0,
|
|
42273
42355
|
input_images: inputImages
|
|
42274
42356
|
});
|
|
42275
|
-
const verb = inputImages ? "Editing image
|
|
42357
|
+
const verb = inputImages ? "Editing image..." : "Generating image...";
|
|
42276
42358
|
const result = opts.json ? await doGenerate() : await withSpinner(verb, doGenerate, { done: null });
|
|
42277
42359
|
const ext = result.content_type.includes("png") ? "png" : "jpg";
|
|
42278
42360
|
const filename = opts.output || `generated.${ext}`;
|
|
@@ -42318,7 +42400,7 @@ Examples:
|
|
|
42318
42400
|
aspect_ratio: opts.aspect,
|
|
42319
42401
|
resolution: opts.resolution
|
|
42320
42402
|
});
|
|
42321
|
-
const result = opts.json ? await doGenerate() : await withSpinner("Generating video
|
|
42403
|
+
const result = opts.json ? await doGenerate() : await withSpinner("Generating video...", doGenerate, { done: null });
|
|
42322
42404
|
const filename = opts.output || "generated.mp4";
|
|
42323
42405
|
const savedPath = await downloadFile(result.url, filename, !!opts.output);
|
|
42324
42406
|
if (opts.json) {
|
|
@@ -42366,7 +42448,7 @@ Examples:
|
|
|
42366
42448
|
language: opts.language,
|
|
42367
42449
|
speakers
|
|
42368
42450
|
});
|
|
42369
|
-
const result = opts.json ? await doGenerate() : await withSpinner("Generating speech
|
|
42451
|
+
const result = opts.json ? await doGenerate() : await withSpinner("Generating speech...", doGenerate, { done: null });
|
|
42370
42452
|
const filename = opts.output || "speech.mp3";
|
|
42371
42453
|
const savedPath = await downloadFile(result.url, filename, !!opts.output);
|
|
42372
42454
|
if (opts.json) {
|
|
@@ -42405,7 +42487,7 @@ Examples:
|
|
|
42405
42487
|
model: opts.model,
|
|
42406
42488
|
instrumental: !opts.vocals
|
|
42407
42489
|
});
|
|
42408
|
-
const result = opts.json ? await doGenerate() : await withSpinner("Generating music
|
|
42490
|
+
const result = opts.json ? await doGenerate() : await withSpinner("Generating music...", doGenerate, { done: null });
|
|
42409
42491
|
const filename = opts.output || "music.mp3";
|
|
42410
42492
|
const savedPath = await downloadFile(result.url, filename, !!opts.output);
|
|
42411
42493
|
if (opts.json) {
|
|
@@ -42443,7 +42525,7 @@ Examples:
|
|
|
42443
42525
|
duration_seconds: opts.duration,
|
|
42444
42526
|
prompt_influence: opts.influence
|
|
42445
42527
|
});
|
|
42446
|
-
const result = opts.json ? await doGenerate() : await withSpinner("Generating sound effect
|
|
42528
|
+
const result = opts.json ? await doGenerate() : await withSpinner("Generating sound effect...", doGenerate, { done: null });
|
|
42447
42529
|
const filename = opts.output || "sound.mp3";
|
|
42448
42530
|
const savedPath = await downloadFile(result.url, filename, !!opts.output);
|
|
42449
42531
|
if (opts.json) {
|
|
@@ -42465,12 +42547,12 @@ init_api();
|
|
|
42465
42547
|
init_config();
|
|
42466
42548
|
init_colors();
|
|
42467
42549
|
import { existsSync as existsSync15, readFileSync as readFileSync20 } from "fs";
|
|
42468
|
-
import { join as
|
|
42550
|
+
import { join as join18 } from "path";
|
|
42469
42551
|
function readInstalledKitReadme(name) {
|
|
42470
42552
|
const root = getProjectRoot() ?? process.cwd();
|
|
42471
|
-
const kitDir =
|
|
42472
|
-
if (!existsSync15(
|
|
42473
|
-
const readme =
|
|
42553
|
+
const kitDir = join18(root, "src", "packages", name);
|
|
42554
|
+
if (!existsSync15(join18(kitDir, "package.json"))) return null;
|
|
42555
|
+
const readme = join18(kitDir, "README.md");
|
|
42474
42556
|
return existsSync15(readme) ? readFileSync20(readme, "utf-8") : null;
|
|
42475
42557
|
}
|
|
42476
42558
|
var skillCommand = new Command("skill").description("Task docs - read the matching skill before building");
|
|
@@ -42645,14 +42727,14 @@ var domainCommand = new Command("domain").description("Manage custom domains").a
|
|
|
42645
42727
|
|
|
42646
42728
|
// src/commands/realtime.ts
|
|
42647
42729
|
import { existsSync as existsSync16 } from "fs";
|
|
42648
|
-
import { dirname as
|
|
42730
|
+
import { dirname as dirname12, resolve as resolve14 } from "path";
|
|
42649
42731
|
init_api();
|
|
42650
42732
|
init_config();
|
|
42651
42733
|
init_colors();
|
|
42652
42734
|
function hasDeployManifest() {
|
|
42653
42735
|
const cfgPath = getConfigPath();
|
|
42654
42736
|
if (!cfgPath) return false;
|
|
42655
|
-
return existsSync16(resolve14(
|
|
42737
|
+
return existsSync16(resolve14(dirname12(cfgPath), "gipity.yaml"));
|
|
42656
42738
|
}
|
|
42657
42739
|
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 () => {
|
|
42658
42740
|
const config = requireConfig();
|
|
@@ -42830,7 +42912,7 @@ async function pollTestStatus(projectGuid, runGuid, opts) {
|
|
|
42830
42912
|
const elapsed = Math.round((now - startTime) / 1e3);
|
|
42831
42913
|
const progress = data.totalFiles === 0 ? "starting up" : `${data.completedFiles}/${data.totalFiles} files`;
|
|
42832
42914
|
const tally = data.passed + data.failed > 0 ? ` (${data.passed} passed${data.failed > 0 ? `, ${data.failed} failed` : ""} so far)` : "";
|
|
42833
|
-
console.log(muted(`
|
|
42915
|
+
console.log(muted(` ... still running - ${progress}${tally}, ${elapsed}s elapsed`));
|
|
42834
42916
|
if (now - startTime >= LONG_RUN_MS && !longRunHintShown) {
|
|
42835
42917
|
longRunHintShown = true;
|
|
42836
42918
|
console.log(muted(" Note: progressing, not hung. LLM-backed tests can take minutes. To verify one function fast, use `gipity fn call <name>`; narrow this suite with `gipity test <path>`."));
|
|
@@ -43061,21 +43143,21 @@ var locationCommand = new Command("location").description("Show location").argum
|
|
|
43061
43143
|
}));
|
|
43062
43144
|
|
|
43063
43145
|
// src/commands/doctor.ts
|
|
43064
|
-
import { existsSync as existsSync18, readFileSync as readFileSync22, statSync as
|
|
43065
|
-
import { join as
|
|
43146
|
+
import { existsSync as existsSync18, readFileSync as readFileSync22, statSync as statSync9 } from "fs";
|
|
43147
|
+
import { join as join20, resolve as resolve15 } from "path";
|
|
43066
43148
|
import { homedir as homedir14 } from "os";
|
|
43067
43149
|
|
|
43068
43150
|
// src/updater/state.ts
|
|
43069
43151
|
import { readFileSync as readFileSync21, writeFileSync as writeFileSync14, mkdirSync as mkdirSync14, existsSync as existsSync17 } from "fs";
|
|
43070
|
-
import { join as
|
|
43152
|
+
import { join as join19 } from "path";
|
|
43071
43153
|
import { homedir as homedir13 } from "os";
|
|
43072
|
-
var GIPITY_DIR =
|
|
43073
|
-
var LOCAL_DIR =
|
|
43074
|
-
var LOCAL_PKG_DIR =
|
|
43075
|
-
var LOCAL_ENTRY =
|
|
43076
|
-
var STATE_FILE =
|
|
43077
|
-
var SETTINGS_FILE =
|
|
43078
|
-
var UPDATE_LOG =
|
|
43154
|
+
var GIPITY_DIR = join19(homedir13(), ".gipity");
|
|
43155
|
+
var LOCAL_DIR = join19(GIPITY_DIR, "local");
|
|
43156
|
+
var LOCAL_PKG_DIR = join19(LOCAL_DIR, "node_modules", "gipity");
|
|
43157
|
+
var LOCAL_ENTRY = join19(LOCAL_PKG_DIR, "dist", "index.js");
|
|
43158
|
+
var STATE_FILE = join19(GIPITY_DIR, "update-state.json");
|
|
43159
|
+
var SETTINGS_FILE = join19(GIPITY_DIR, "settings.json");
|
|
43160
|
+
var UPDATE_LOG = join19(GIPITY_DIR, "update.log");
|
|
43079
43161
|
var DEFAULT_STATE = {
|
|
43080
43162
|
installedVersion: null,
|
|
43081
43163
|
lastCheckAt: 0,
|
|
@@ -43136,7 +43218,7 @@ function versionManagerNode(execPath, env = process.env, home = homedir14()) {
|
|
|
43136
43218
|
return null;
|
|
43137
43219
|
}
|
|
43138
43220
|
function localVersion() {
|
|
43139
|
-
const pkgPath =
|
|
43221
|
+
const pkgPath = join20(LOCAL_PKG_DIR, "package.json");
|
|
43140
43222
|
if (!existsSync18(pkgPath)) return null;
|
|
43141
43223
|
try {
|
|
43142
43224
|
return JSON.parse(readFileSync22(pkgPath, "utf-8")).version ?? null;
|
|
@@ -43250,7 +43332,7 @@ var doctorCommand = new Command("doctor").description("Check install + environme
|
|
|
43250
43332
|
console.log(`${muted("last check ")} ${rel(state.lastCheckAt)}`);
|
|
43251
43333
|
console.log(`${muted("last error ")} ${state.lastError ? error(state.lastError) : dim("none")}`);
|
|
43252
43334
|
console.log(`${muted("state file ")} ${existsSync18(STATE_FILE) ? STATE_FILE : dim("(none yet)")}`);
|
|
43253
|
-
console.log(`${muted("update log ")} ${existsSync18(UPDATE_LOG) ? `${UPDATE_LOG} (${
|
|
43335
|
+
console.log(`${muted("update log ")} ${existsSync18(UPDATE_LOG) ? `${UPDATE_LOG} (${statSync9(UPDATE_LOG).size} bytes)` : dim("(none yet)")}`);
|
|
43254
43336
|
console.log("");
|
|
43255
43337
|
console.log(dim("Force an update with: gipity update"));
|
|
43256
43338
|
});
|
|
@@ -43259,10 +43341,10 @@ var doctorCommand = new Command("doctor").description("Check install + environme
|
|
|
43259
43341
|
import { appendFileSync } from "fs";
|
|
43260
43342
|
|
|
43261
43343
|
// src/updater/install.ts
|
|
43262
|
-
import { existsSync as existsSync19, mkdirSync as mkdirSync15, rmSync as rmSync2, statSync as
|
|
43263
|
-
import { dirname as
|
|
43344
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync15, rmSync as rmSync2, statSync as statSync10, unlinkSync as unlinkSync5, writeFileSync as writeFileSync15 } from "fs";
|
|
43345
|
+
import { dirname as dirname13, join as join21 } from "path";
|
|
43264
43346
|
init_platform();
|
|
43265
|
-
var UPDATE_LOCK =
|
|
43347
|
+
var UPDATE_LOCK = join21(GIPITY_DIR, "update.lock");
|
|
43266
43348
|
var LOCK_STALE_MS2 = 10 * 60 * 1e3;
|
|
43267
43349
|
function npmInstallGipity(version) {
|
|
43268
43350
|
const res = spawnSyncCommand(resolveCommand("npm"), ["install", "--no-audit", "--no-fund", "--ignore-scripts", `gipity@${version}`], {
|
|
@@ -43283,18 +43365,18 @@ function isWedged(stderr) {
|
|
|
43283
43365
|
}
|
|
43284
43366
|
function resetLocalTree(dir = LOCAL_DIR) {
|
|
43285
43367
|
mkdirSync15(dir, { recursive: true });
|
|
43286
|
-
rmSync2(
|
|
43287
|
-
rmSync2(
|
|
43288
|
-
writeFileSync15(
|
|
43368
|
+
rmSync2(join21(dir, "node_modules"), { recursive: true, force: true });
|
|
43369
|
+
rmSync2(join21(dir, "package-lock.json"), { force: true });
|
|
43370
|
+
writeFileSync15(join21(dir, "package.json"), JSON.stringify({ name: "gipity-local", private: true, version: "0.0.0" }, null, 2));
|
|
43289
43371
|
}
|
|
43290
43372
|
function acquireUpdateLock(lockPath2 = UPDATE_LOCK) {
|
|
43291
|
-
mkdirSync15(
|
|
43373
|
+
mkdirSync15(dirname13(lockPath2), { recursive: true });
|
|
43292
43374
|
try {
|
|
43293
43375
|
writeFileSync15(lockPath2, String(process.pid), { flag: "wx" });
|
|
43294
43376
|
return true;
|
|
43295
43377
|
} catch {
|
|
43296
43378
|
try {
|
|
43297
|
-
if (Date.now() -
|
|
43379
|
+
if (Date.now() - statSync10(lockPath2).mtimeMs > LOCK_STALE_MS2) {
|
|
43298
43380
|
unlinkSync5(lockPath2);
|
|
43299
43381
|
writeFileSync15(lockPath2, String(process.pid), { flag: "wx" });
|
|
43300
43382
|
return true;
|
|
@@ -43455,7 +43537,7 @@ import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync17, existsSync
|
|
|
43455
43537
|
import { stat, readFile } from "fs/promises";
|
|
43456
43538
|
import { createInterface as createInterface2 } from "readline";
|
|
43457
43539
|
import { homedir as homedir16, hostname as hostname5, platform as osPlatform3, loadavg as loadavg2, freemem as freemem2, totalmem as totalmem2, cpus as cpus2 } from "os";
|
|
43458
|
-
import { join as
|
|
43540
|
+
import { join as join24 } from "path";
|
|
43459
43541
|
init_auth();
|
|
43460
43542
|
init_api();
|
|
43461
43543
|
|
|
@@ -44211,8 +44293,8 @@ async function revokeRelayAgentToken() {
|
|
|
44211
44293
|
init_platform();
|
|
44212
44294
|
init_client_context();
|
|
44213
44295
|
import { cpus, freemem, totalmem, loadavg, release, uptime, homedir as homedir15 } from "os";
|
|
44214
|
-
import { statfsSync, readdirSync as
|
|
44215
|
-
import { join as
|
|
44296
|
+
import { statfsSync, readdirSync as readdirSync9 } from "fs";
|
|
44297
|
+
import { join as join22 } from "path";
|
|
44216
44298
|
var PROBE_TIMEOUT_MS = 4e3;
|
|
44217
44299
|
function parseVersion(out) {
|
|
44218
44300
|
const m = out.match(/\d+\.\d+(?:\.\d+)?(?:[-.\w]*)?/);
|
|
@@ -44279,7 +44361,7 @@ async function detectGpu() {
|
|
|
44279
44361
|
function diskUsage() {
|
|
44280
44362
|
try {
|
|
44281
44363
|
if (typeof statfsSync !== "function") return void 0;
|
|
44282
|
-
const st2 = statfsSync(
|
|
44364
|
+
const st2 = statfsSync(join22(homedir15(), "GipityProjects"), { bigint: false });
|
|
44283
44365
|
if (!st2 || !st2.bsize) return void 0;
|
|
44284
44366
|
return { total: st2.bsize * st2.blocks, free: st2.bsize * st2.bavail };
|
|
44285
44367
|
} catch {
|
|
@@ -44293,7 +44375,7 @@ function diskUsage() {
|
|
|
44293
44375
|
}
|
|
44294
44376
|
function localProjectCount() {
|
|
44295
44377
|
try {
|
|
44296
|
-
return
|
|
44378
|
+
return readdirSync9(join22(homedir15(), "GipityProjects"), { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).length;
|
|
44297
44379
|
} catch {
|
|
44298
44380
|
return void 0;
|
|
44299
44381
|
}
|
|
@@ -44602,7 +44684,7 @@ var SessionPool = class {
|
|
|
44602
44684
|
// src/relay/daemon.ts
|
|
44603
44685
|
init_config();
|
|
44604
44686
|
init_api();
|
|
44605
|
-
var RELAY_LOG_PATH =
|
|
44687
|
+
var RELAY_LOG_PATH = join24(homedir16(), ".gipity", "relay.log");
|
|
44606
44688
|
var HEARTBEAT_INTERVAL_MS = parseInt(process.env.GIPITY_RELAY_HEARTBEAT_MS || "60000", 10);
|
|
44607
44689
|
var DIAGNOSTICS_INTERVAL_MS = parseInt(process.env.GIPITY_RELAY_DIAGNOSTICS_MS || String(24 * 60 * 60 * 1e3), 10);
|
|
44608
44690
|
var LONG_POLL_TIMEOUT_MS = parseInt(process.env.GIPITY_RELAY_POLL_TIMEOUT_MS || "35000", 10);
|
|
@@ -44719,7 +44801,7 @@ function log2(level, msg, extra) {
|
|
|
44719
44801
|
const pretty = `${C7.dim(hhmmss())} ${badge(level)} ${C7.bold(msg)}${formatExtra(extra)}`;
|
|
44720
44802
|
process.stderr.write(pretty + "\n");
|
|
44721
44803
|
try {
|
|
44722
|
-
const dir =
|
|
44804
|
+
const dir = join24(homedir16(), ".gipity");
|
|
44723
44805
|
mkdirSync17(dir, { recursive: true });
|
|
44724
44806
|
lockLogPerms(dir, RELAY_LOG_PATH);
|
|
44725
44807
|
const json = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level, msg, ...extra ?? {} });
|
|
@@ -45120,7 +45202,7 @@ function isSafeSessionId(s) {
|
|
|
45120
45202
|
function transcriptPathFor(cwd, sessionId) {
|
|
45121
45203
|
if (!isSafeSessionId(sessionId)) return null;
|
|
45122
45204
|
const slug = cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
45123
|
-
return
|
|
45205
|
+
return join24(homedir16(), ".claude", "projects", slug, `${sessionId}.jsonl`);
|
|
45124
45206
|
}
|
|
45125
45207
|
function formatDuration(ms2) {
|
|
45126
45208
|
const totalSec = ms2 / 1e3;
|
|
@@ -45574,8 +45656,8 @@ async function resolveCwdForProject(d) {
|
|
|
45574
45656
|
throw new Error(`Invalid project slug: ${JSON.stringify(d.project_slug)}`);
|
|
45575
45657
|
}
|
|
45576
45658
|
const root = getProjectsRoot();
|
|
45577
|
-
const path5 =
|
|
45578
|
-
const configPath =
|
|
45659
|
+
const path5 = join24(root, d.project_slug);
|
|
45660
|
+
const configPath = join24(path5, ".gipity.json");
|
|
45579
45661
|
if (existsSync21(configPath)) {
|
|
45580
45662
|
try {
|
|
45581
45663
|
const cfg = JSON.parse(readFileSync24(configPath, "utf-8"));
|
|
@@ -45713,7 +45795,7 @@ async function spawnSync2(cwd, timeoutMs, onSpawn) {
|
|
|
45713
45795
|
} catch {
|
|
45714
45796
|
}
|
|
45715
45797
|
try {
|
|
45716
|
-
unlinkSync7(
|
|
45798
|
+
unlinkSync7(join24(cwd, ".gipity", "sync.lock"));
|
|
45717
45799
|
} catch {
|
|
45718
45800
|
}
|
|
45719
45801
|
finish(() => reject(new Error(`timed out after ${Math.round(timeoutMs / 1e3)}s`)));
|
|
@@ -46351,9 +46433,7 @@ var connectCommand = new Command("connect").alias("setup").description("Connect
|
|
|
46351
46433
|
console.log("");
|
|
46352
46434
|
const enabled = await runRelaySetup({ mode: "run-now" });
|
|
46353
46435
|
if (enabled) {
|
|
46354
|
-
|
|
46355
|
-
console.log(` ${success("Done")} \u2014 your relay ${running2 ? "is running in the background" : "is set up"} and will start with your computer.`);
|
|
46356
|
-
console.log(` ${muted("Open")} ${brand("gipity.ai")} ${muted("and start a chat to drive your coding agent here. Manage it with `gipity relay status`.")}`);
|
|
46436
|
+
console.log(` ${muted("Manage it anytime with `gipity relay status`.")}`);
|
|
46357
46437
|
} else {
|
|
46358
46438
|
console.log(` ${muted("No relay set up. Run `gipity connect` again anytime, or `gipity build` to start building.")}`);
|
|
46359
46439
|
}
|
|
@@ -46373,9 +46453,9 @@ init_utils();
|
|
|
46373
46453
|
init_colors();
|
|
46374
46454
|
import { existsSync as existsSync23, rmSync as rmSync4, unlinkSync as unlinkSync9, readFileSync as readFileSync26, writeFileSync as writeFileSync17 } from "fs";
|
|
46375
46455
|
import { homedir as homedir17, platform as osPlatform4 } from "os";
|
|
46376
|
-
import { join as
|
|
46456
|
+
import { join as join25, resolve as resolve17 } from "path";
|
|
46377
46457
|
function removeGipityPluginConfig() {
|
|
46378
|
-
const settingsPath =
|
|
46458
|
+
const settingsPath = join25(homedir17(), ".claude", "settings.json");
|
|
46379
46459
|
if (!existsSync23(settingsPath)) return false;
|
|
46380
46460
|
let settings;
|
|
46381
46461
|
try {
|
|
@@ -46402,7 +46482,7 @@ function removeInstallerPathLines() {
|
|
|
46402
46482
|
const marker = "# Added by the Gipity installer";
|
|
46403
46483
|
const touched = [];
|
|
46404
46484
|
for (const name of [".bashrc", ".zshrc", ".profile"]) {
|
|
46405
|
-
const rc2 =
|
|
46485
|
+
const rc2 = join25(homedir17(), name);
|
|
46406
46486
|
if (!existsSync23(rc2)) continue;
|
|
46407
46487
|
let text;
|
|
46408
46488
|
try {
|
|
@@ -46485,8 +46565,8 @@ async function revokeDeviceBestEffort() {
|
|
|
46485
46565
|
}
|
|
46486
46566
|
var uninstallCommand = new Command("uninstall").description("Uninstall Gipity").option("--yes", "Skip confirmation prompts").action(async (opts) => {
|
|
46487
46567
|
const autoYes = opts.yes || getAutoConfirm();
|
|
46488
|
-
const gipityDir =
|
|
46489
|
-
const launcherBin =
|
|
46568
|
+
const gipityDir = join25(homedir17(), ".gipity");
|
|
46569
|
+
const launcherBin = join25(gipityDir, "launcher", "bin", "gipity");
|
|
46490
46570
|
const installedViaLauncher = existsSync23(launcherBin);
|
|
46491
46571
|
console.log(`${bold("Gipity uninstall")} - this will:`);
|
|
46492
46572
|
console.log(`\u2022 Stop the running relay daemon (if any)`);
|
|
@@ -46533,7 +46613,7 @@ var uninstallCommand = new Command("uninstall").description("Uninstall Gipity").
|
|
|
46533
46613
|
if (agentSkills.skills.length) {
|
|
46534
46614
|
for (const name of agentSkills.skills) {
|
|
46535
46615
|
try {
|
|
46536
|
-
rmSync4(
|
|
46616
|
+
rmSync4(join25(AGENTS_SKILLS_DIR, name), { recursive: true, force: true });
|
|
46537
46617
|
} catch {
|
|
46538
46618
|
}
|
|
46539
46619
|
}
|
|
@@ -46685,7 +46765,7 @@ gmailCommand.command("send").description("Send a new email from your Gmail").req
|
|
|
46685
46765
|
const recap = opts.to.join(", ") + (opts.cc.length ? `, cc: ${opts.cc.join(", ")}` : "");
|
|
46686
46766
|
printResult(`Sent from your Gmail to ${recap}.`, opts, { sent: true, to: opts.to });
|
|
46687
46767
|
}));
|
|
46688
|
-
gmailCommand.command("reply").description("Reply to a Gmail thread. Get thread-id + message-id from `gmail search`.").requiredOption("--thread-id <id>", "Thread ID (from `gmail search`)").requiredOption("--message-id <id>", "Message ID being replied to (In-Reply-To)").requiredOption("--to <email>", "Recipient (repeatable)", collect2, []).option("--cc <email>", "Cc recipient (repeatable)", collect2, []).option("--bcc <email>", "Bcc recipient (repeatable)", collect2, []).option("--reply-to <email>", "Reply-To header address").requiredOption("--subject <s>", 'Subject (usually "Re:
|
|
46768
|
+
gmailCommand.command("reply").description("Reply to a Gmail thread. Get thread-id + message-id from `gmail search`.").requiredOption("--thread-id <id>", "Thread ID (from `gmail search`)").requiredOption("--message-id <id>", "Message ID being replied to (In-Reply-To)").requiredOption("--to <email>", "Recipient (repeatable)", collect2, []).option("--cc <email>", "Cc recipient (repeatable)", collect2, []).option("--bcc <email>", "Bcc recipient (repeatable)", collect2, []).option("--reply-to <email>", "Reply-To header address").requiredOption("--subject <s>", 'Subject (usually "Re: ..." of the original)').requiredOption("--body <text>", "Reply body (you compose the quote header)").option("--json", "Output as JSON").action((opts) => run("Reply", async () => {
|
|
46689
46769
|
if (!opts.to.length) throw new Error("--to is required (pass at least one)");
|
|
46690
46770
|
const payload = {
|
|
46691
46771
|
thread_id: opts.threadId,
|
|
@@ -47067,7 +47147,7 @@ function collectRealFlags(argv2, program3) {
|
|
|
47067
47147
|
|
|
47068
47148
|
// src/trace.ts
|
|
47069
47149
|
import { openSync as openSync5, writeSync, mkdirSync as mkdirSync18 } from "fs";
|
|
47070
|
-
import { join as
|
|
47150
|
+
import { join as join26 } from "path";
|
|
47071
47151
|
import { homedir as homedir18 } from "os";
|
|
47072
47152
|
var TRACE_KEY = /* @__PURE__ */ Symbol.for("gipity.traceOutput");
|
|
47073
47153
|
function installOutputTrace(label2) {
|
|
@@ -47079,10 +47159,10 @@ function installOutputTrace(label2) {
|
|
|
47079
47159
|
existing.emit({ event: "reenter", label: label2 });
|
|
47080
47160
|
return;
|
|
47081
47161
|
}
|
|
47082
|
-
const dir =
|
|
47162
|
+
const dir = join26(process.env.GIPITY_DIR || join26(homedir18(), ".gipity"), "trace");
|
|
47083
47163
|
mkdirSync18(dir, { recursive: true });
|
|
47084
47164
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", "_").replace(/:/g, "-");
|
|
47085
|
-
const fd2 = openSync5(
|
|
47165
|
+
const fd2 = openSync5(join26(dir, `${stamp}-pid${process.pid}.jsonl`), "a");
|
|
47086
47166
|
const emit = (rec) => {
|
|
47087
47167
|
try {
|
|
47088
47168
|
writeSync(fd2, JSON.stringify({ t: (/* @__PURE__ */ new Date()).toISOString(), ...rec }) + "\n");
|
|
@@ -47117,7 +47197,7 @@ function installOutputTrace(label2) {
|
|
|
47117
47197
|
|
|
47118
47198
|
// src/index.ts
|
|
47119
47199
|
installOutputTrace("index");
|
|
47120
|
-
var __dirname =
|
|
47200
|
+
var __dirname = dirname14(fileURLToPath3(import.meta.url));
|
|
47121
47201
|
var pkg = JSON.parse(readFileSync28(resolve18(__dirname, "../package.json"), "utf-8"));
|
|
47122
47202
|
function versionLabel() {
|
|
47123
47203
|
const base = `v${pkg.version}`;
|
|
@@ -47336,7 +47416,7 @@ Command.prototype._excessArguments = function(receivedArgs) {
|
|
|
47336
47416
|
let message = `error: unexpected extra argument${excess.length === 1 ? "" : "s"} ${list}${forSubcommand}.`;
|
|
47337
47417
|
const kv2 = excess.map((a) => /^([A-Za-z][\w-]*)=/.exec(a)).find(Boolean);
|
|
47338
47418
|
if (kv2) {
|
|
47339
|
-
message += ` Options are passed as \`--${kv2[1]} <value>\`, not \`${kv2[1]}
|
|
47419
|
+
message += ` Options are passed as \`--${kv2[1]} <value>\`, not \`${kv2[1]}=...\` - did you mean \`--${kv2[1]}\`?`;
|
|
47340
47420
|
}
|
|
47341
47421
|
this.error(message, { code: "commander.excessArguments" });
|
|
47342
47422
|
};
|