gipity 1.1.5 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/agy.js +52 -0
- package/dist/agents/index.js +2 -0
- package/dist/api.js +19 -3
- package/dist/capture/sources/agy.js +88 -0
- package/dist/client-context.js +9 -0
- package/dist/commands/brand.js +125 -0
- package/dist/commands/build.js +1 -1
- package/dist/commands/fn.js +2 -8
- package/dist/commands/init.js +2 -2
- package/dist/commands/key.js +91 -0
- package/dist/commands/page-eval.js +42 -3
- package/dist/commands/records.js +57 -19
- package/dist/commands/token.js +6 -1
- package/dist/commands/uninstall.js +14 -1
- package/dist/hooks/capture-runner.js +20 -8
- package/dist/index.js +553 -105
- package/dist/knowledge.js +1 -1
- package/dist/project-setup.js +2 -0
- package/dist/relay/diagnostics.js +4 -2
- package/dist/setup.js +198 -16
- package/dist/template-vars.js +74 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6597,6 +6597,9 @@ function detectHarness() {
|
|
|
6597
6597
|
}
|
|
6598
6598
|
if (hasEnvPrefix("CODEX_")) return { harness: "codex" };
|
|
6599
6599
|
if (hasEnvPrefix("GROK_")) return { harness: "grok", harnessSession: process.env.GROK_SESSION_ID };
|
|
6600
|
+
if (process.env.ANTIGRAVITY_CONVERSATION_ID) {
|
|
6601
|
+
return { harness: "agy", harnessSession: process.env.ANTIGRAVITY_CONVERSATION_ID };
|
|
6602
|
+
}
|
|
6600
6603
|
if (process.env.CURSOR_TRACE_ID || hasEnvPrefix("CURSOR_") || (process.env.TERM_PROGRAM ?? "").toLowerCase().includes("cursor")) {
|
|
6601
6604
|
return { harness: "cursor" };
|
|
6602
6605
|
}
|
|
@@ -6643,11 +6646,13 @@ __export(api_exports, {
|
|
|
6643
6646
|
getAccountSlug: () => getAccountSlug,
|
|
6644
6647
|
getAuthHeader: () => getAuthHeader,
|
|
6645
6648
|
getBaseUrl: () => getBaseUrl,
|
|
6649
|
+
mintAppToken: () => mintAppToken,
|
|
6646
6650
|
patch: () => patch,
|
|
6647
6651
|
post: () => post,
|
|
6648
6652
|
postBinary: () => postBinary,
|
|
6649
6653
|
postForTarEntries: () => postForTarEntries,
|
|
6650
6654
|
publicPost: () => publicPost,
|
|
6655
|
+
publicRequest: () => publicRequest,
|
|
6651
6656
|
put: () => put,
|
|
6652
6657
|
putTimeoutMs: () => putTimeoutMs,
|
|
6653
6658
|
putToPresignedUrl: () => putToPresignedUrl,
|
|
@@ -6903,12 +6908,12 @@ async function getAccountSlug() {
|
|
|
6903
6908
|
accountSlugCache = res.data.accountSlug;
|
|
6904
6909
|
return accountSlugCache;
|
|
6905
6910
|
}
|
|
6906
|
-
async function
|
|
6911
|
+
async function publicRequest(method, path5, body, extraHeaders) {
|
|
6907
6912
|
const url = `${baseUrl()}${path5}`;
|
|
6908
6913
|
const res = await fetch(url, {
|
|
6909
|
-
method
|
|
6914
|
+
method,
|
|
6910
6915
|
headers: { ...clientHeaders(), "Content-Type": "application/json", ...extraHeaders },
|
|
6911
|
-
body: JSON.stringify(body)
|
|
6916
|
+
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
6912
6917
|
});
|
|
6913
6918
|
if (!res.ok) {
|
|
6914
6919
|
const json = await res.json().catch(() => ({ error: { code: "UNKNOWN", message: res.statusText } }));
|
|
@@ -6917,6 +6922,17 @@ async function publicPost(path5, body, extraHeaders) {
|
|
|
6917
6922
|
}
|
|
6918
6923
|
return res.json();
|
|
6919
6924
|
}
|
|
6925
|
+
function publicPost(path5, body, extraHeaders) {
|
|
6926
|
+
return publicRequest("POST", path5, body, extraHeaders);
|
|
6927
|
+
}
|
|
6928
|
+
async function mintAppToken(projectGuid2) {
|
|
6929
|
+
try {
|
|
6930
|
+
const minted = await publicPost("/api/token", { app: projectGuid2 });
|
|
6931
|
+
return { "X-App-Token": minted.data.token };
|
|
6932
|
+
} catch {
|
|
6933
|
+
return void 0;
|
|
6934
|
+
}
|
|
6935
|
+
}
|
|
6920
6936
|
var tar, ApiError, REQUEST_TIMEOUT_MS, DOWNLOAD_HEADER_TIMEOUT_MS, PUT_TIMEOUT_FLOOR_MS, PUT_MIN_BYTES_PER_SEC, accountSlugCache;
|
|
6921
6937
|
var init_api = __esm({
|
|
6922
6938
|
"src/api.ts"() {
|
|
@@ -32075,7 +32091,7 @@ mkdir -p ~/GipityProjects/<slug> && cd ~/GipityProjects/<slug> && gipity init <s
|
|
|
32075
32091
|
|
|
32076
32092
|
## CLI quick reference
|
|
32077
32093
|
|
|
32078
|
-
Key commands: \`gipity add <template|kit>\`, \`gipity deploy dev\`, \`gipity sandbox run\`, \`gipity page inspect <url>\`, \`gipity page screenshot <url>\`, \`gipity db query "SQL"\`, \`gipity fn call <name>\`, \`gipity logs fn <name>\`, \`gipity secrets set <NAME> <value> [--account]\` (store an API key/token encrypted; read in functions via \`secrets.get('NAME')\` \u2014 never hardcode keys), \`gipity email send --to <addr> --subject <s> --body <b>\` (sends as \`gipity@gipity.ai\`; omit \`--to\` to self-send), \`gipity skill read <name>\`.
|
|
32094
|
+
Key commands: \`gipity add <template|kit>\`, \`gipity brand set --emoji <e>|--color <hex>\` (regenerate the app's icons + social share card), \`gipity deploy dev\`, \`gipity sandbox run\`, \`gipity page inspect <url>\`, \`gipity page screenshot <url>\`, \`gipity db query "SQL"\`, \`gipity fn call <name>\`, \`gipity logs fn <name>\`, \`gipity secrets set <NAME> <value> [--account]\` (store an API key/token encrypted; read in functions via \`secrets.get('NAME')\` \u2014 never hardcode keys), \`gipity email send --to <addr> --subject <s> --body <b>\` (sends as \`gipity@gipity.ai\`; omit \`--to\` to self-send), \`gipity skill read <name>\`.
|
|
32079
32095
|
Rename for findability: \`gipity project rename <name>\` renames the current project's display name (the slug and deployed URLs never change); \`gipity chat rename <title>\` renames the current chat's tab title. Both are the display label users scan to switch between tabs \u2014 retitle a chat when the conversation clearly shifts to a new topic (sparingly, not every turn), and keep every project/chat title SHORT: 2-4 words, \u226440 characters, no trailing punctuation (e.g. "Stripe checkout", "Tetris game").
|
|
32080
32096
|
Pull an existing remote project local (given its URL/slug): \`mkdir -p ~/GipityProjects/<slug> && cd ~/GipityProjects/<slug> && gipity init <slug>\` (adopts the matching project and syncs files down - this is the "clone").
|
|
32081
32097
|
Move whole apps in/out: \`gipity save\` (export this project as a portable \`.gip\` bundle), \`gipity load <file.gip | github:owner/repo>\` (import as a NEW project; \`--inspect\` to preview), \`gipity github connect\` (1-2 click GitHub access for imports). Porting a Vercel/Replit/Lovable app? Load the \`app-import\` skill first.
|
|
@@ -32513,7 +32529,7 @@ var UploadConflictError = class extends Error {
|
|
|
32513
32529
|
currentServerVersion;
|
|
32514
32530
|
path;
|
|
32515
32531
|
};
|
|
32516
|
-
async function uploadOneFile(
|
|
32532
|
+
async function uploadOneFile(projectGuid2, localPath, virtualPath, opts = {}) {
|
|
32517
32533
|
const { sha256, size } = await hashFile(localPath);
|
|
32518
32534
|
const mime = opts.mime ?? guessMime(virtualPath);
|
|
32519
32535
|
const initBody = { path: virtualPath, size, sha256, mime };
|
|
@@ -32523,7 +32539,7 @@ async function uploadOneFile(projectGuid, localPath, virtualPath, opts = {}) {
|
|
|
32523
32539
|
let init;
|
|
32524
32540
|
try {
|
|
32525
32541
|
init = await post(
|
|
32526
|
-
`/projects/${
|
|
32542
|
+
`/projects/${projectGuid2}/files/upload-init`,
|
|
32527
32543
|
initBody
|
|
32528
32544
|
);
|
|
32529
32545
|
} catch (err) {
|
|
@@ -32545,7 +32561,7 @@ async function uploadOneFile(projectGuid, localPath, virtualPath, opts = {}) {
|
|
|
32545
32561
|
}
|
|
32546
32562
|
let comp;
|
|
32547
32563
|
try {
|
|
32548
|
-
comp = await post(`/projects/${
|
|
32564
|
+
comp = await post(`/projects/${projectGuid2}/files/upload-complete`, completeBody);
|
|
32549
32565
|
} catch (err) {
|
|
32550
32566
|
if (err instanceof ApiError && err.statusCode === 409) {
|
|
32551
32567
|
const current = typeof err.data?.current_server_version === "number" ? err.data.current_server_version : null;
|
|
@@ -32627,16 +32643,16 @@ async function transferToS3(localPath, size, mime, data, opts = {}) {
|
|
|
32627
32643
|
completed.sort((a, b7) => a.part_number - b7.part_number);
|
|
32628
32644
|
return { parts: completed };
|
|
32629
32645
|
}
|
|
32630
|
-
async function uploadInitBatch(
|
|
32646
|
+
async function uploadInitBatch(projectGuid2, files) {
|
|
32631
32647
|
const res = await post(
|
|
32632
|
-
`/projects/${
|
|
32648
|
+
`/projects/${projectGuid2}/files/upload-init-batch`,
|
|
32633
32649
|
{ files }
|
|
32634
32650
|
);
|
|
32635
32651
|
return res.data.results;
|
|
32636
32652
|
}
|
|
32637
|
-
async function uploadCompleteBatch(
|
|
32653
|
+
async function uploadCompleteBatch(projectGuid2, items) {
|
|
32638
32654
|
const res = await post(
|
|
32639
|
-
`/projects/${
|
|
32655
|
+
`/projects/${projectGuid2}/files/upload-complete-batch`,
|
|
32640
32656
|
{ items }
|
|
32641
32657
|
);
|
|
32642
32658
|
return res.data.results;
|
|
@@ -32653,6 +32669,8 @@ var PRIMER_FILES = {
|
|
|
32653
32669
|
codex: "AGENTS.md",
|
|
32654
32670
|
grok: "AGENTS.md",
|
|
32655
32671
|
// Grok Build reads the AGENTS.md family (and CLAUDE.md) natively
|
|
32672
|
+
agy: "AGENTS.md",
|
|
32673
|
+
// Antigravity reads the same AGENTS.md/GEMINI.md rules family natively
|
|
32656
32674
|
aider: "AGENTS.md",
|
|
32657
32675
|
// shares the Codex primer; aider is pointed at it via .aider.conf.yml
|
|
32658
32676
|
gemini: "GEMINI.md",
|
|
@@ -32668,6 +32686,7 @@ var DEFAULT_SYNC_IGNORE = [
|
|
|
32668
32686
|
".gipity/",
|
|
32669
32687
|
".claude/",
|
|
32670
32688
|
".codex/",
|
|
32689
|
+
".agents/",
|
|
32671
32690
|
".gitignore",
|
|
32672
32691
|
AIDER_CONF_FILE,
|
|
32673
32692
|
// Home-directory junk: a project created inside a real home dir (or one that
|
|
@@ -32867,9 +32886,11 @@ function ensureGrokPluginInstalled() {
|
|
|
32867
32886
|
var AGENTS_SKILLS_DIR = join4(homedir3(), ".agents", "skills");
|
|
32868
32887
|
var AGENT_HOOKS_DIR = join4(homedir3(), ".gipity", "agent-hooks");
|
|
32869
32888
|
var AGENT_SKILLS_MANIFEST = join4(homedir3(), ".gipity", "agent-skills.json");
|
|
32870
|
-
|
|
32889
|
+
var AGY_SKILLS_DIR = join4(homedir3(), ".gemini", "config", "skills");
|
|
32890
|
+
var AGY_SKILLS_MANIFEST = join4(homedir3(), ".gipity", "agy-skills.json");
|
|
32891
|
+
function skillsManifestState(manifestPath) {
|
|
32871
32892
|
try {
|
|
32872
|
-
const m = JSON.parse(readFileSync6(
|
|
32893
|
+
const m = JSON.parse(readFileSync6(manifestPath, "utf-8"));
|
|
32873
32894
|
return {
|
|
32874
32895
|
current: typeof m?.version === "string" && versionGte(m.version, GIPITY_PLUGIN_VERSION),
|
|
32875
32896
|
skills: Array.isArray(m?.skills) ? m.skills : []
|
|
@@ -32878,8 +32899,13 @@ function agentSkillsState() {
|
|
|
32878
32899
|
return { current: false, skills: [] };
|
|
32879
32900
|
}
|
|
32880
32901
|
}
|
|
32881
|
-
function
|
|
32882
|
-
|
|
32902
|
+
function agentSkillsState() {
|
|
32903
|
+
return skillsManifestState(AGENT_SKILLS_MANIFEST);
|
|
32904
|
+
}
|
|
32905
|
+
function agySkillsState() {
|
|
32906
|
+
return skillsManifestState(AGY_SKILLS_MANIFEST);
|
|
32907
|
+
}
|
|
32908
|
+
function installSkillsAndHooks(skillsDir, manifestPath, harnessLabel) {
|
|
32883
32909
|
if (!binaryOnPath("git")) return;
|
|
32884
32910
|
const tmp = mkdtempSync(join4(tmpdir(), "gipity-skills-"));
|
|
32885
32911
|
try {
|
|
@@ -32901,8 +32927,8 @@ function ensureAgentSkillsInstalled() {
|
|
|
32901
32927
|
for (const entry of readdirSync3(skillsSrc, { withFileTypes: true })) {
|
|
32902
32928
|
if (!entry.isDirectory()) continue;
|
|
32903
32929
|
if (!existsSync4(join4(skillsSrc, entry.name, "SKILL.md"))) continue;
|
|
32904
|
-
mkdirSync3(
|
|
32905
|
-
cpSync(join4(skillsSrc, entry.name), join4(
|
|
32930
|
+
mkdirSync3(skillsDir, { recursive: true });
|
|
32931
|
+
cpSync(join4(skillsSrc, entry.name), join4(skillsDir, entry.name), {
|
|
32906
32932
|
recursive: true,
|
|
32907
32933
|
force: true
|
|
32908
32934
|
});
|
|
@@ -32912,13 +32938,21 @@ function ensureAgentSkillsInstalled() {
|
|
|
32912
32938
|
for (const script of readdirSync3(join4(repo, "hooks", "scripts"))) {
|
|
32913
32939
|
cpSync(join4(repo, "hooks", "scripts", script), join4(AGENT_HOOKS_DIR, script), { force: true });
|
|
32914
32940
|
}
|
|
32915
|
-
writeFileSync4(
|
|
32916
|
-
console.log(`Installed ${names.length} Gipity skills for
|
|
32941
|
+
writeFileSync4(manifestPath, JSON.stringify({ version, skills: names }, null, 2) + "\n");
|
|
32942
|
+
console.log(`Installed ${names.length} Gipity skills for ${harnessLabel} (${skillsDir}).`);
|
|
32917
32943
|
} catch {
|
|
32918
32944
|
} finally {
|
|
32919
32945
|
rmSync(tmp, { recursive: true, force: true });
|
|
32920
32946
|
}
|
|
32921
32947
|
}
|
|
32948
|
+
function ensureAgentSkillsInstalled() {
|
|
32949
|
+
if (agentSkillsState().current) return;
|
|
32950
|
+
installSkillsAndHooks(AGENTS_SKILLS_DIR, AGENT_SKILLS_MANIFEST, "Codex");
|
|
32951
|
+
}
|
|
32952
|
+
function ensureAgySkillsInstalled() {
|
|
32953
|
+
if (agySkillsState().current) return;
|
|
32954
|
+
installSkillsAndHooks(AGY_SKILLS_DIR, AGY_SKILLS_MANIFEST, "Antigravity");
|
|
32955
|
+
}
|
|
32922
32956
|
function applyCodexHooks(existing) {
|
|
32923
32957
|
const launcher = join4(AGENT_HOOKS_DIR, "launch.sh");
|
|
32924
32958
|
const cmd = (script, ...args) => [`sh "${launcher}" "${join4(AGENT_HOOKS_DIR, script)}"`, ...args].join(" ");
|
|
@@ -32976,6 +33010,102 @@ function setupCodexIntegration() {
|
|
|
32976
33010
|
ensureAgentSkillsInstalled();
|
|
32977
33011
|
setupCodexHooks();
|
|
32978
33012
|
}
|
|
33013
|
+
var AGY_HOOKS_SCRIPT = `#!/usr/bin/env node
|
|
33014
|
+
'use strict';
|
|
33015
|
+
const { spawnSync } = require('child_process');
|
|
33016
|
+
const { join } = require('path');
|
|
33017
|
+
|
|
33018
|
+
const WRITE_TOOLS = new Set(['write_to_file', 'replace_file_content']);
|
|
33019
|
+
|
|
33020
|
+
function readStdin() {
|
|
33021
|
+
return new Promise((res) => {
|
|
33022
|
+
let data = '';
|
|
33023
|
+
process.stdin.setEncoding('utf-8');
|
|
33024
|
+
process.stdin.on('data', (c) => { data += c; });
|
|
33025
|
+
process.stdin.on('end', () => res(data));
|
|
33026
|
+
process.stdin.on('error', () => res(data));
|
|
33027
|
+
});
|
|
33028
|
+
}
|
|
33029
|
+
|
|
33030
|
+
async function main() {
|
|
33031
|
+
const event = process.argv[2];
|
|
33032
|
+
const raw = await readStdin();
|
|
33033
|
+
let payload = {};
|
|
33034
|
+
try { payload = JSON.parse(raw); } catch { /* keep {} */ }
|
|
33035
|
+
|
|
33036
|
+
try {
|
|
33037
|
+
if (event === 'post-tool-use') {
|
|
33038
|
+
const toolCall = payload.toolCall;
|
|
33039
|
+
const filePath = toolCall && toolCall.args && toolCall.args.TargetFile;
|
|
33040
|
+
if (toolCall && WRITE_TOOLS.has(toolCall.name) && typeof filePath === 'string' && filePath) {
|
|
33041
|
+
spawnSync(process.execPath, [join(__dirname, 'sync-push.cjs')], {
|
|
33042
|
+
input: JSON.stringify({ tool_input: { file_path: filePath } }),
|
|
33043
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
33044
|
+
windowsHide: true,
|
|
33045
|
+
});
|
|
33046
|
+
}
|
|
33047
|
+
spawnSync(process.execPath, [join(__dirname, 'capture.cjs'), 'agy', 'post-tool-use'], {
|
|
33048
|
+
input: raw,
|
|
33049
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
33050
|
+
windowsHide: true,
|
|
33051
|
+
});
|
|
33052
|
+
} else if (event === 'stop') {
|
|
33053
|
+
spawnSync(process.execPath, [join(__dirname, 'capture.cjs'), 'agy', 'stop'], {
|
|
33054
|
+
input: raw,
|
|
33055
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
33056
|
+
windowsHide: true,
|
|
33057
|
+
});
|
|
33058
|
+
}
|
|
33059
|
+
} catch { /* never let a side effect break the response below */ }
|
|
33060
|
+
|
|
33061
|
+
process.stdout.write('{}');
|
|
33062
|
+
}
|
|
33063
|
+
|
|
33064
|
+
main();
|
|
33065
|
+
`;
|
|
33066
|
+
function applyAgyHooks(existing) {
|
|
33067
|
+
const wrapper = join4(AGENT_HOOKS_DIR, "agy-hooks.cjs");
|
|
33068
|
+
const launcher = join4(AGENT_HOOKS_DIR, "launch.sh");
|
|
33069
|
+
const wrapCmd = (event) => `sh "${launcher}" "${wrapper}" ${event}`;
|
|
33070
|
+
const block = {
|
|
33071
|
+
PostToolUse: [
|
|
33072
|
+
{ matcher: ".*", hooks: [{ type: "command", command: wrapCmd("post-tool-use"), timeout: 30 }] }
|
|
33073
|
+
],
|
|
33074
|
+
Stop: [
|
|
33075
|
+
{ hooks: [{ type: "command", command: wrapCmd("stop"), timeout: 60 }] }
|
|
33076
|
+
]
|
|
33077
|
+
};
|
|
33078
|
+
let settings = {};
|
|
33079
|
+
if (existing !== null) {
|
|
33080
|
+
try {
|
|
33081
|
+
settings = JSON.parse(existing);
|
|
33082
|
+
} catch {
|
|
33083
|
+
return null;
|
|
33084
|
+
}
|
|
33085
|
+
}
|
|
33086
|
+
if (JSON.stringify(settings.gipity ?? null) === JSON.stringify(block)) return null;
|
|
33087
|
+
settings.gipity = block;
|
|
33088
|
+
return JSON.stringify(settings, null, 2) + "\n";
|
|
33089
|
+
}
|
|
33090
|
+
function setupAgyHooks() {
|
|
33091
|
+
if (process.platform === "win32") return;
|
|
33092
|
+
const cwd = resolve3(process.cwd());
|
|
33093
|
+
if (cwd === resolve3(homedir3())) return;
|
|
33094
|
+
mkdirSync3(AGENT_HOOKS_DIR, { recursive: true });
|
|
33095
|
+
writeFileSync4(join4(AGENT_HOOKS_DIR, "agy-hooks.cjs"), AGY_HOOKS_SCRIPT);
|
|
33096
|
+
const path5 = join4(cwd, ".agents", "hooks.json");
|
|
33097
|
+
const existing = existsSync4(path5) ? readFileSync6(path5, "utf-8") : null;
|
|
33098
|
+
const next = applyAgyHooks(existing);
|
|
33099
|
+
if (next === null) return;
|
|
33100
|
+
mkdirSync3(dirname3(path5), { recursive: true });
|
|
33101
|
+
writeFileSync4(path5, next);
|
|
33102
|
+
console.log(existing === null ? "Wrote Antigravity sync + session-capture hooks (.agents/hooks.json)." : "Updated Antigravity hooks (.agents/hooks.json).");
|
|
33103
|
+
}
|
|
33104
|
+
function setupAgyIntegration() {
|
|
33105
|
+
if (!binaryOnPath("agy")) return;
|
|
33106
|
+
ensureAgySkillsInstalled();
|
|
33107
|
+
setupAgyHooks();
|
|
33108
|
+
}
|
|
32979
33109
|
function setupClaudeHooks() {
|
|
32980
33110
|
ensureGipityPlugin();
|
|
32981
33111
|
const cwd = resolve3(process.cwd());
|
|
@@ -33097,6 +33227,7 @@ var SUPPORTED_TOOLS = [
|
|
|
33097
33227
|
{ key: "claude", label: "Claude Code (CLAUDE.md + Gipity plugin)", setup: setupClaudeMd, integrate: setupClaudeHooks },
|
|
33098
33228
|
{ key: "codex", label: "OpenAI Codex (AGENTS.md + skills + sync hooks)", setup: setupAgentsMd, integrate: setupCodexIntegration },
|
|
33099
33229
|
{ key: "grok", label: "Grok Build (AGENTS.md + Gipity plugin)", setup: setupAgentsMd, integrate: ensureGrokPluginInstalled },
|
|
33230
|
+
{ key: "agy", label: "Antigravity (AGENTS.md + skills + sync hooks)", setup: setupAgentsMd, integrate: setupAgyIntegration },
|
|
33100
33231
|
{ key: "aider", label: "Aider (AGENTS.md + .aider.conf.yml)", setup: setupAiderMd, optIn: true },
|
|
33101
33232
|
{ key: "gemini", label: "Gemini CLI (GEMINI.md)", setup: setupGeminiMd },
|
|
33102
33233
|
{ key: "copilot", label: "GitHub Copilot (.github/copilot-instructions.md)", setup: setupCopilotMd },
|
|
@@ -33226,22 +33357,22 @@ async function acquireLock(progress) {
|
|
|
33226
33357
|
}
|
|
33227
33358
|
}
|
|
33228
33359
|
}
|
|
33229
|
-
function readBaseline(
|
|
33360
|
+
function readBaseline(projectGuid2) {
|
|
33230
33361
|
const path5 = syncStatePath();
|
|
33231
|
-
if (!existsSync5(path5)) return { projectGuid, files: {}, lastFullSync: null };
|
|
33362
|
+
if (!existsSync5(path5)) return { projectGuid: projectGuid2, files: {}, lastFullSync: null };
|
|
33232
33363
|
try {
|
|
33233
33364
|
const parsed = JSON.parse(readFileSync7(path5, "utf-8"));
|
|
33234
|
-
if (parsed.projectGuid !==
|
|
33235
|
-
return { projectGuid, files: {}, lastFullSync: null };
|
|
33365
|
+
if (parsed.projectGuid !== projectGuid2) {
|
|
33366
|
+
return { projectGuid: projectGuid2, files: {}, lastFullSync: null };
|
|
33236
33367
|
}
|
|
33237
33368
|
return {
|
|
33238
|
-
projectGuid,
|
|
33369
|
+
projectGuid: projectGuid2,
|
|
33239
33370
|
files: parsed.files ?? {},
|
|
33240
33371
|
lastFullSync: parsed.lastFullSync ?? null,
|
|
33241
33372
|
...parsed.deletesSkippedStreak ? { deletesSkippedStreak: parsed.deletesSkippedStreak } : {}
|
|
33242
33373
|
};
|
|
33243
33374
|
} catch {
|
|
33244
|
-
return { projectGuid, files: {}, lastFullSync: null };
|
|
33375
|
+
return { projectGuid: projectGuid2, files: {}, lastFullSync: null };
|
|
33245
33376
|
}
|
|
33246
33377
|
}
|
|
33247
33378
|
function writeBaseline(b7) {
|
|
@@ -33314,8 +33445,8 @@ function resolveInRoot(root, relPath) {
|
|
|
33314
33445
|
}
|
|
33315
33446
|
return full;
|
|
33316
33447
|
}
|
|
33317
|
-
async function fetchRemote(
|
|
33318
|
-
const res = await get(`/projects/${
|
|
33448
|
+
async function fetchRemote(projectGuid2) {
|
|
33449
|
+
const res = await get(`/projects/${projectGuid2}/files/tree`);
|
|
33319
33450
|
const out = /* @__PURE__ */ new Map();
|
|
33320
33451
|
for (const f of res.data) {
|
|
33321
33452
|
if (f.type !== "file") continue;
|
|
@@ -33376,14 +33507,14 @@ function extractTarToMap(stream, idleMs, onBytes, keep) {
|
|
|
33376
33507
|
stream.pipe(extract3);
|
|
33377
33508
|
});
|
|
33378
33509
|
}
|
|
33379
|
-
async function downloadAll(
|
|
33380
|
-
const stream = await downloadStream(`/projects/${
|
|
33510
|
+
async function downloadAll(projectGuid2, onBytes) {
|
|
33511
|
+
const stream = await downloadStream(`/projects/${projectGuid2}/files/tree?content=tar`);
|
|
33381
33512
|
return extractTarToMap(stream, DOWNLOAD_IDLE_MS, onBytes);
|
|
33382
33513
|
}
|
|
33383
|
-
async function fetchOneOutcome(
|
|
33514
|
+
async function fetchOneOutcome(projectGuid2, path5, expectedSha) {
|
|
33384
33515
|
try {
|
|
33385
33516
|
const stream = await downloadStream(
|
|
33386
|
-
`/projects/${
|
|
33517
|
+
`/projects/${projectGuid2}/files/tree?content=tar&path=${encodeURIComponent(path5)}`
|
|
33387
33518
|
);
|
|
33388
33519
|
const want = normalizeTreePath(path5);
|
|
33389
33520
|
const files = await extractTarToMap(stream, DOWNLOAD_IDLE_MS, void 0, (p) => p === want);
|
|
@@ -33399,8 +33530,8 @@ async function fetchOneOutcome(projectGuid, path5, expectedSha) {
|
|
|
33399
33530
|
return { kind: "transient" };
|
|
33400
33531
|
}
|
|
33401
33532
|
}
|
|
33402
|
-
async function fetchOne(
|
|
33403
|
-
const out = await fetchOneOutcome(
|
|
33533
|
+
async function fetchOne(projectGuid2, path5, expectedSha) {
|
|
33534
|
+
const out = await fetchOneOutcome(projectGuid2, path5, expectedSha);
|
|
33404
33535
|
return out.kind === "ok" ? out.buf : null;
|
|
33405
33536
|
}
|
|
33406
33537
|
function classifyLocal(info2, base) {
|
|
@@ -33632,14 +33763,14 @@ async function sync(opts = {}) {
|
|
|
33632
33763
|
releaseLock();
|
|
33633
33764
|
}
|
|
33634
33765
|
}
|
|
33635
|
-
async function syncInner(
|
|
33636
|
-
const config = { projectGuid, ignore: ignore2 };
|
|
33766
|
+
async function syncInner(projectGuid2, root, ignore2, opts, interactive) {
|
|
33767
|
+
const config = { projectGuid: projectGuid2, ignore: ignore2 };
|
|
33637
33768
|
const p = opts.progress;
|
|
33638
|
-
const baseline = readBaseline(
|
|
33769
|
+
const baseline = readBaseline(projectGuid2);
|
|
33639
33770
|
p?.phase("Scanning local files...");
|
|
33640
33771
|
const local = walkLocal(root, ignore2, baseline.files);
|
|
33641
33772
|
p?.phase("Checking Gipity for changes...");
|
|
33642
|
-
const remote = await fetchRemote(
|
|
33773
|
+
const remote = await fetchRemote(projectGuid2);
|
|
33643
33774
|
for (const path5 of [...remote.keys()]) {
|
|
33644
33775
|
if (shouldIgnore(path5, ignore2)) remote.delete(path5);
|
|
33645
33776
|
}
|
|
@@ -34439,7 +34570,12 @@ function parseDuration(raw, unit) {
|
|
|
34439
34570
|
}
|
|
34440
34571
|
|
|
34441
34572
|
// src/commands/token.ts
|
|
34442
|
-
var tokenCommand = new Command("token").description("Manage API tokens").addHelpText("after",
|
|
34573
|
+
var tokenCommand = new Command("token").description("Manage API tokens").addHelpText("after", `
|
|
34574
|
+
Long-lived agent API tokens (gip_at_*) for headless agents and CI - they drive
|
|
34575
|
+
the gipity CLI as YOU.
|
|
34576
|
+
|
|
34577
|
+
To let a script or cron write to one app instead, mint a project API key:
|
|
34578
|
+
gipity key create "my script" --role editor (sent as X-Api-Key).`);
|
|
34443
34579
|
var fmtDate = (d) => d ? new Date(d).toLocaleDateString() : "never";
|
|
34444
34580
|
tokenCommand.command("create").description("Mint a long-lived agent API token (shown once)").option("--name <name>", 'Label for the token, e.g. "Hermes on my VPS"').option("--expires <days>", "Days until the token expires (default: never)").option("--json", "Output as JSON").action((opts) => run("Create", async () => {
|
|
34445
34581
|
const body = {};
|
|
@@ -34484,6 +34620,73 @@ tokenCommand.command("revoke <short_guid>").alias("rm").description("Revoke an a
|
|
|
34484
34620
|
console.log(success(`Revoked token ${bold(shortGuid)}.`));
|
|
34485
34621
|
}));
|
|
34486
34622
|
|
|
34623
|
+
// src/commands/key.ts
|
|
34624
|
+
init_api();
|
|
34625
|
+
init_config();
|
|
34626
|
+
init_colors();
|
|
34627
|
+
var keyCommand = new Command("key").description("Manage project API keys for scripts and agents (X-Api-Key)").addHelpText("after", `
|
|
34628
|
+
Give a script, cron, or agent write access to your app without a login:
|
|
34629
|
+
|
|
34630
|
+
gipity key create "laptop importer" --role editor
|
|
34631
|
+
# the script sends: X-Api-Key: <the key printed once above>
|
|
34632
|
+
|
|
34633
|
+
Inside a function the caller shows up as ctx.auth.via === 'api_key' with
|
|
34634
|
+
ctx.auth.apiKeyName set to the key's name - stamp that on rows to tell
|
|
34635
|
+
script-written entries from hand-entered ones. Never build your own key table.
|
|
34636
|
+
|
|
34637
|
+
Account-level tokens that run the CLI headlessly are a different thing: see
|
|
34638
|
+
gipity token --help.`);
|
|
34639
|
+
var fmtDate2 = (d) => d ? new Date(d).toLocaleDateString() : "never";
|
|
34640
|
+
var projectOpt = ["--project <guid-or-slug>", "Target a specific project instead of cwd / Home"];
|
|
34641
|
+
async function projectGuid(opts) {
|
|
34642
|
+
const { config } = await resolveProjectContext({ projectOverride: opts.project });
|
|
34643
|
+
return config.projectGuid;
|
|
34644
|
+
}
|
|
34645
|
+
keyCommand.command("create <name>").description("Mint a project API key (shown once). Give it a name you will recognize later").option("--role <role>", "viewer | editor | owner (default: viewer)", "viewer").option("--expires-days <n>", "Days until the key expires (default: never)").option(...projectOpt).option("--json", "Output as JSON").action((name, opts) => run("Create", async () => {
|
|
34646
|
+
const body = { name, role: opts.role };
|
|
34647
|
+
if (opts.expiresDays !== void 0) {
|
|
34648
|
+
const days = parseInt(opts.expiresDays, 10);
|
|
34649
|
+
if (!Number.isFinite(days) || days <= 0) throw new Error("--expires-days must be a positive number of days");
|
|
34650
|
+
body.expires_in_days = days;
|
|
34651
|
+
}
|
|
34652
|
+
const res = await post(
|
|
34653
|
+
`/projects/${await projectGuid(opts)}/api-keys`,
|
|
34654
|
+
body
|
|
34655
|
+
);
|
|
34656
|
+
const k7 = res.data;
|
|
34657
|
+
if (opts.json) {
|
|
34658
|
+
console.log(JSON.stringify(k7));
|
|
34659
|
+
return;
|
|
34660
|
+
}
|
|
34661
|
+
const expNote = k7.expires_at ? ` (expires ${fmtDate2(k7.expires_at)})` : " (never expires)";
|
|
34662
|
+
console.log(success(`Created API key ${bold(k7.short_guid)} "${k7.name}" as ${k7.role}${muted(expNote)}.`));
|
|
34663
|
+
console.log("");
|
|
34664
|
+
console.log(k7.key);
|
|
34665
|
+
console.log("");
|
|
34666
|
+
console.log(muted("Send it from your script on every request:"));
|
|
34667
|
+
console.log(muted(` curl -H "X-Api-Key: ${k7.key}" ...`));
|
|
34668
|
+
console.log(muted(`Revoke it any time: gipity key revoke ${k7.short_guid}`));
|
|
34669
|
+
console.log("");
|
|
34670
|
+
console.log(warning("Copy it now - it will not be shown again."));
|
|
34671
|
+
}));
|
|
34672
|
+
keyCommand.command("list").alias("ls").description("List this project's API keys (values are never shown again)").option(...projectOpt).option("--json", "Output as JSON").action((opts) => run("List", async () => {
|
|
34673
|
+
const res = await get(`/projects/${await projectGuid(opts)}/api-keys`);
|
|
34674
|
+
printList(
|
|
34675
|
+
res.data,
|
|
34676
|
+
opts,
|
|
34677
|
+
'No API keys. Mint one with: gipity key create "my script" --role editor',
|
|
34678
|
+
(k7) => `${bold(k7.short_guid)} ${k7.name} ${muted(`${k7.role} ${k7.prefix}\u2026 last used ${fmtDate2(k7.last_used_at)} expires ${fmtDate2(k7.expires_at)}`)}`
|
|
34679
|
+
);
|
|
34680
|
+
}));
|
|
34681
|
+
keyCommand.command("revoke <short_guid>").alias("rm").description("Revoke a project API key (instant, irreversible)").option(...projectOpt).option("--json", "Output as JSON").action((shortGuid, opts) => run("Revoke", async () => {
|
|
34682
|
+
await del(`/projects/${await projectGuid(opts)}/api-keys/${encodeURIComponent(shortGuid)}`);
|
|
34683
|
+
if (opts.json) {
|
|
34684
|
+
console.log(JSON.stringify({ short_guid: shortGuid, revoked: true }));
|
|
34685
|
+
return;
|
|
34686
|
+
}
|
|
34687
|
+
console.log(success(`Revoked API key ${bold(shortGuid)}.`));
|
|
34688
|
+
}));
|
|
34689
|
+
|
|
34487
34690
|
// src/commands/init.ts
|
|
34488
34691
|
init_api();
|
|
34489
34692
|
init_config();
|
|
@@ -34529,6 +34732,80 @@ function escapeHtml(s) {
|
|
|
34529
34732
|
function jsEscape(s) {
|
|
34530
34733
|
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
34531
34734
|
}
|
|
34735
|
+
var ACCENT_PALETTE = [
|
|
34736
|
+
"#f59e0b",
|
|
34737
|
+
"#f97316",
|
|
34738
|
+
"#ef4444",
|
|
34739
|
+
"#ec4899",
|
|
34740
|
+
"#a855f7",
|
|
34741
|
+
"#6366f1",
|
|
34742
|
+
"#3b82f6",
|
|
34743
|
+
"#0ea5e9",
|
|
34744
|
+
"#14b8a6",
|
|
34745
|
+
"#22c55e",
|
|
34746
|
+
"#84cc16",
|
|
34747
|
+
"#eab308"
|
|
34748
|
+
];
|
|
34749
|
+
function hashString(s) {
|
|
34750
|
+
let h = 2166136261;
|
|
34751
|
+
for (let i = 0; i < s.length; i++) {
|
|
34752
|
+
h ^= s.charCodeAt(i);
|
|
34753
|
+
h = Math.imul(h, 16777619);
|
|
34754
|
+
}
|
|
34755
|
+
return h >>> 0;
|
|
34756
|
+
}
|
|
34757
|
+
function darkTint(hex) {
|
|
34758
|
+
const n = parseInt(hex.slice(1), 16);
|
|
34759
|
+
const base = 1052692;
|
|
34760
|
+
const ch = (shift) => {
|
|
34761
|
+
const a = n >> shift & 255;
|
|
34762
|
+
const b7 = base >> shift & 255;
|
|
34763
|
+
return Math.round(b7 + (a - b7) * 0.14).toString(16).padStart(2, "0");
|
|
34764
|
+
};
|
|
34765
|
+
return `#${ch(16)}${ch(8)}${ch(0)}`;
|
|
34766
|
+
}
|
|
34767
|
+
function buildHeadBlock(v7) {
|
|
34768
|
+
const t = escapeHtml(v7.projectName);
|
|
34769
|
+
const d = v7.description ? escapeHtml(v7.description) : "";
|
|
34770
|
+
const url = v7.accountSlug && v7.projectSlug ? `https://app.gipity.ai/${v7.accountSlug}/${v7.projectSlug}/` : void 0;
|
|
34771
|
+
const themeColor = darkTint(ACCENT_PALETTE[hashString(v7.projectGuid || v7.projectName) % ACCENT_PALETTE.length]);
|
|
34772
|
+
const jsonLd = JSON.stringify({
|
|
34773
|
+
"@context": "https://schema.org",
|
|
34774
|
+
"@type": "WebApplication",
|
|
34775
|
+
name: v7.projectName,
|
|
34776
|
+
...v7.description ? { description: v7.description } : {},
|
|
34777
|
+
...url ? { url } : {}
|
|
34778
|
+
}, null, 2).replace(/<\//g, "<\\/");
|
|
34779
|
+
const lines = [
|
|
34780
|
+
`<title>${t}</title>`,
|
|
34781
|
+
...d ? [`<meta name="description" content="${d}">`] : [],
|
|
34782
|
+
...url ? [`<link rel="canonical" href="${url}">`] : [],
|
|
34783
|
+
`<meta property="og:title" content="${t}">`,
|
|
34784
|
+
`<meta property="og:type" content="website">`,
|
|
34785
|
+
...d ? [`<meta property="og:description" content="${d}">`] : [],
|
|
34786
|
+
...url ? [
|
|
34787
|
+
`<meta property="og:url" content="${url}">`,
|
|
34788
|
+
`<meta property="og:image" content="${url}images/og-image.png">`,
|
|
34789
|
+
`<meta property="og:image:width" content="1200">`,
|
|
34790
|
+
`<meta property="og:image:height" content="630">`
|
|
34791
|
+
] : [],
|
|
34792
|
+
`<meta name="twitter:card" content="summary_large_image">`,
|
|
34793
|
+
`<meta name="twitter:title" content="${t}">`,
|
|
34794
|
+
...d ? [`<meta name="twitter:description" content="${d}">`] : [],
|
|
34795
|
+
...url ? [`<meta name="twitter:image" content="${url}images/og-image.png">`] : [],
|
|
34796
|
+
`<meta name="theme-color" content="${themeColor}">`,
|
|
34797
|
+
`<link rel="icon" type="image/png" sizes="192x192" href="./images/favicon-192.png">`,
|
|
34798
|
+
`<link rel="icon" type="image/png" sizes="512x512" href="./images/favicon-512.png">`,
|
|
34799
|
+
`<link rel="icon" type="image/x-icon" href="./images/favicon.ico">`,
|
|
34800
|
+
`<link rel="apple-touch-icon" href="./images/apple-touch-icon.png">`,
|
|
34801
|
+
`<link rel="manifest" href="./manifest.webmanifest">`,
|
|
34802
|
+
`<script type="application/ld+json">
|
|
34803
|
+
${jsonLd}
|
|
34804
|
+
</script>`
|
|
34805
|
+
];
|
|
34806
|
+
return lines.map((l7) => `
|
|
34807
|
+
${l7}`).join("");
|
|
34808
|
+
}
|
|
34532
34809
|
function buildTemplateVars(v7) {
|
|
34533
34810
|
const safeTitle = escapeHtml(v7.projectName);
|
|
34534
34811
|
const safeDesc = v7.description ? escapeHtml(v7.description) : "";
|
|
@@ -34544,6 +34821,9 @@ function buildTemplateVars(v7) {
|
|
|
34544
34821
|
"{{JS_TITLE}}": jsEscape(v7.projectName),
|
|
34545
34822
|
"{{PROJECT_GUID}}": v7.projectGuid,
|
|
34546
34823
|
"{{DATABASE}}": slug,
|
|
34824
|
+
"{{HEAD_BLOCK}}": buildHeadBlock(v7),
|
|
34825
|
+
// Legacy per-tag placeholders — current templates carry only {{HEAD_BLOCK}},
|
|
34826
|
+
// but older local template copies may still reference these.
|
|
34547
34827
|
"{{DESCRIPTION_META}}": v7.description ? `
|
|
34548
34828
|
<meta name="description" content="${safeDesc}">` : "",
|
|
34549
34829
|
"{{OG_DESCRIPTION}}": v7.description ? `
|
|
@@ -34638,7 +34918,9 @@ async function finalizeLocalProject(opts) {
|
|
|
34638
34918
|
try {
|
|
34639
34919
|
const sub = await substituteDir(opts.dir, {
|
|
34640
34920
|
projectGuid: opts.projectGuid,
|
|
34641
|
-
projectName: opts.projectName
|
|
34921
|
+
projectName: opts.projectName,
|
|
34922
|
+
accountSlug: opts.accountSlug,
|
|
34923
|
+
projectSlug: opts.projectSlug
|
|
34642
34924
|
});
|
|
34643
34925
|
if (sub.changed.length) {
|
|
34644
34926
|
console.log(muted(`Resolved template vars in ${sub.changed.length} file${sub.changed.length > 1 ? "s" : ""}.`));
|
|
@@ -35024,7 +35306,7 @@ function resolveTools(forFlag) {
|
|
|
35024
35306
|
(t) => requested.includes(t.key) || !t.optIn && requested.includes("all")
|
|
35025
35307
|
);
|
|
35026
35308
|
}
|
|
35027
|
-
var initCommand = new Command("init").description("Link this directory to a project").addHelpText("after", "\nWrites CLAUDE.md/AGENTS.md primer files so your AI coding tool understands Gipity, and installs the Gipity skills + file-sync hooks into the agent CLIs found on this machine (Claude Code, Codex, Grok).").argument("[name]", "Project name/slug (defaults to current directory name)").option("--agent <guid>", "Agent GUID to use").option("--no-capture", "Don't record Claude Code sessions in this directory to your Gipity project (sets captureHooks: false in .gipity.json)").option(
|
|
35309
|
+
var initCommand = new Command("init").description("Link this directory to a project").addHelpText("after", "\nWrites CLAUDE.md/AGENTS.md primer files so your AI coding tool understands Gipity, and installs the Gipity skills + file-sync hooks into the agent CLIs found on this machine (Claude Code, Codex, Grok, Antigravity).").argument("[name]", "Project name/slug (defaults to current directory name)").option("--agent <guid>", "Agent GUID to use").option("--no-capture", "Don't record Claude Code sessions in this directory to your Gipity project (sets captureHooks: false in .gipity.json)").option(
|
|
35028
35310
|
"--for <tools>",
|
|
35029
35311
|
`Which AI tool primer files to write (comma-separated). Default: all except aider (opt-in - it also writes .aider.conf.yml). Choices: ${TOOL_KEYS.join(", ")}, all`
|
|
35030
35312
|
).addHelpText("after", `
|
|
@@ -35152,7 +35434,7 @@ Working with an existing Gipity project:
|
|
|
35152
35434
|
}
|
|
35153
35435
|
console.log(success(`Wrote primer files: ${primerSummary}.`));
|
|
35154
35436
|
if (wantsClaude) {
|
|
35155
|
-
console.log(success("Ready! Run your coding agent here (claude, codex, grok), or `gipity build` to launch one with a picker."));
|
|
35437
|
+
console.log(success("Ready! Run your coding agent here (claude, codex, grok, agy), or `gipity build` to launch one with a picker."));
|
|
35156
35438
|
if (opts.capture === false) {
|
|
35157
35439
|
console.log(muted("Session recording is off for this project (captureHooks: false in .gipity.json)."));
|
|
35158
35440
|
} else {
|
|
@@ -35490,13 +35772,13 @@ Nothing named "${basename4(localPath)}" under ${root} either. Generate a frame w
|
|
|
35490
35772
|
`${flag} ${localPath}: no such file \u2014 looked for ${abs} (relative paths resolve against the current directory, ${process.cwd()}).${found}`
|
|
35491
35773
|
);
|
|
35492
35774
|
}
|
|
35493
|
-
async function uploadPublicFixture(
|
|
35775
|
+
async function uploadPublicFixture(projectGuid2, localPath, flag = "--fixture") {
|
|
35494
35776
|
assertLocalAsset(flag, localPath);
|
|
35495
35777
|
const name = basename4(localPath);
|
|
35496
35778
|
const size = statSync6(localPath).size;
|
|
35497
35779
|
const contentType = guessMime(localPath);
|
|
35498
35780
|
const init = await post(
|
|
35499
|
-
`/api/${
|
|
35781
|
+
`/api/${projectGuid2}/uploads/init`,
|
|
35500
35782
|
{ filename: name, content_type: contentType, size, public: true }
|
|
35501
35783
|
);
|
|
35502
35784
|
if (init.data.method !== "PUT" || !init.data.url) {
|
|
@@ -35511,13 +35793,13 @@ async function uploadPublicFixture(projectGuid, localPath, flag = "--fixture") {
|
|
|
35511
35793
|
throw new Error(`upload of fixture "${name}" failed: ${res.status} ${res.statusText}`);
|
|
35512
35794
|
}
|
|
35513
35795
|
const done = await post(
|
|
35514
|
-
`/api/${
|
|
35796
|
+
`/api/${projectGuid2}/uploads/complete`,
|
|
35515
35797
|
{ upload_guid: init.data.upload_guid }
|
|
35516
35798
|
);
|
|
35517
35799
|
return { guid: done.data.guid, url: done.data.url, name, localPath };
|
|
35518
35800
|
}
|
|
35519
|
-
async function deleteFixture(
|
|
35520
|
-
await del(`/api/${
|
|
35801
|
+
async function deleteFixture(projectGuid2, guid) {
|
|
35802
|
+
await del(`/api/${projectGuid2}/uploads/${guid}`);
|
|
35521
35803
|
}
|
|
35522
35804
|
var CAMERA_EXTS = [".png", ".jpg", ".jpeg", ".webp", ".mp4", ".webm", ".y4m", ".mjpeg"];
|
|
35523
35805
|
function assertCameraFile(localPath) {
|
|
@@ -35530,9 +35812,9 @@ A still image is played as a looping single-frame feed, which is what a gesture/
|
|
|
35530
35812
|
No frame to hand? Generate one: gipity generate image "a hand making a closed fist, palm to camera, plain background"`
|
|
35531
35813
|
);
|
|
35532
35814
|
}
|
|
35533
|
-
async function uploadCameraFeed(
|
|
35815
|
+
async function uploadCameraFeed(projectGuid2, localPath) {
|
|
35534
35816
|
assertCameraFile(localPath);
|
|
35535
|
-
return uploadPublicFixture(
|
|
35817
|
+
return uploadPublicFixture(projectGuid2, localPath, "--camera");
|
|
35536
35818
|
}
|
|
35537
35819
|
|
|
35538
35820
|
// src/commands/page-eval.ts
|
|
@@ -35655,7 +35937,15 @@ function budgetOverrunHint(reason, usedBudgetMs) {
|
|
|
35655
35937
|
}
|
|
35656
35938
|
return `That budget was ${Math.round(usedBudgetMs / 1e3)}s. Raise it with --timeout <ms> (up to ${EVAL_SCRIPT_BUDGET_MAX_MS}), e.g. --timeout ${EVAL_SCRIPT_BUDGET_MAX_MS} \u2014 or gate on a ready signal instead: --wait-for '<selector>' --wait-timeout ${MAX_WAIT_MS}.`;
|
|
35657
35939
|
}
|
|
35940
|
+
function navigationAbortHint(reason) {
|
|
35941
|
+
if (!/navigated or reloaded/i.test(reason)) return null;
|
|
35942
|
+
return `If you were verifying that state SURVIVES a reload, that is what --reload is for (one command, one page): gipity page eval "<url>" '<seed/assert expr>' --reload '<assert-restored expr>'. It reloads the page in place (localStorage/sessionStorage/cookies preserved) and reports the second result separately. Splitting into two 'page eval' calls does NOT do this: each call is its own browser session, so the second one starts from empty storage and never exercises the restore path.`;
|
|
35943
|
+
}
|
|
35944
|
+
function stepsDeterministically(script) {
|
|
35945
|
+
return /\badvance\s*\(/.test(script);
|
|
35946
|
+
}
|
|
35658
35947
|
function slowRenderMessage(fps, o) {
|
|
35948
|
+
if (!o.camera && stepsDeterministically(o.script ?? "")) return null;
|
|
35659
35949
|
if (o.camera) {
|
|
35660
35950
|
const frames = Math.max(1, Math.round(fps * (o.waitMs / 1e3)));
|
|
35661
35951
|
return `${warning("\u26A0 Slow render:")} page painted at ${fps} fps, so the app's vision pipeline ran on roughly ${bold(`${frames} frame${frames === 1 ? "" : "s"}`)} during the ${Math.round(o.waitMs / 1e3)}s before this eval (it infers once per painted frame). ${bold("That is enough:")} --camera loops your still image, so every one of those frames is the SAME pixels and the model returns the SAME answer on each \u2014 re-running with a bigger --wait/--timeout cannot change the result. ${bold("Do not escalate the wait.")} If a detection landed, it is real. If nothing was detected, the suspect is the ${bold("frame")} (a model needs the whole subject in shot \u2014 a tight crop, an odd angle or a busy background reads as nothing) or the ${bold("app")} (frames never reach the model) \u2014 never the frame rate. Settle it in ONE run: ${CAMERA_FRAME_CHECK}`;
|
|
@@ -35713,7 +36003,7 @@ var pageEvalCommand = new Command("eval").description("Evaluate JS in a real bro
|
|
|
35713
36003
|
[]
|
|
35714
36004
|
).option(
|
|
35715
36005
|
"--reload <expr>",
|
|
35716
|
-
|
|
36006
|
+
'After the first eval, reload the page IN PLACE (localStorage/sessionStorage/cookies preserved) and evaluate this second expression against the post-reload DOM. One command verifies persisted state survives a reload ("remember it when I come back"): seed/assert state with <expr>, then assert the restored UI here. This is the ONLY way to check that - reloading inside the body aborts the eval (the result is lost with the old page), and two separate `page eval` calls are two fresh browser profiles, so the second starts from empty storage.'
|
|
35717
36007
|
).option("--reload-file <path>", "Read the post-reload expression from a file instead of inline --reload (mutually exclusive)").option(
|
|
35718
36008
|
"--camera <path>",
|
|
35719
36009
|
`Play a local image or video (.png/.jpg/.webp/.mp4/.webm/.y4m/.mjpeg) as the browser's WEBCAM feed, so a camera app's real pipeline (getUserMedia \u2192 MediaPipe/YOLOX \u2192 your app logic) runs headlessly on a frame you choose. Implies --fake-media and waits ${CAMERA_DEFAULT_WAIT_MS / 1e3}s for the vision model to load. No frame handy? gipity generate image "a hand making a closed fist, palm to camera".`
|
|
@@ -35797,23 +36087,23 @@ var pageEvalCommand = new Command("eval").description("Evaluate JS in a real bro
|
|
|
35797
36087
|
const fixturePaths = opts.fixture ?? [];
|
|
35798
36088
|
const hosted = [];
|
|
35799
36089
|
let camera;
|
|
35800
|
-
let
|
|
36090
|
+
let projectGuid2;
|
|
35801
36091
|
let sentExpr = expr;
|
|
35802
36092
|
let sentSteps = steps;
|
|
35803
36093
|
if (opts.camera) assertCameraFile(opts.camera);
|
|
35804
36094
|
try {
|
|
35805
36095
|
if (fixturePaths.length || opts.camera) {
|
|
35806
36096
|
const { config } = await resolveProjectContext({});
|
|
35807
|
-
|
|
36097
|
+
projectGuid2 = config.projectGuid;
|
|
35808
36098
|
}
|
|
35809
36099
|
if (opts.camera) {
|
|
35810
36100
|
console.log(muted(`Hosting camera feed ${opts.camera}...`));
|
|
35811
|
-
camera = await uploadCameraFeed(
|
|
36101
|
+
camera = await uploadCameraFeed(projectGuid2, opts.camera);
|
|
35812
36102
|
}
|
|
35813
36103
|
if (fixturePaths.length) {
|
|
35814
36104
|
for (const p of fixturePaths) {
|
|
35815
36105
|
console.log(muted(`Hosting fixture ${p}...`));
|
|
35816
|
-
hosted.push(await uploadPublicFixture(
|
|
36106
|
+
hosted.push(await uploadPublicFixture(projectGuid2, p));
|
|
35817
36107
|
}
|
|
35818
36108
|
const map = {};
|
|
35819
36109
|
for (const h of hosted) map[h.name] = h.url;
|
|
@@ -35851,7 +36141,7 @@ var pageEvalCommand = new Command("eval").description("Evaluate JS in a real bro
|
|
|
35851
36141
|
}));
|
|
35852
36142
|
} catch (err) {
|
|
35853
36143
|
const msg = err?.message ?? "";
|
|
35854
|
-
const hint = budgetOverrunHint(msg, scriptBudgetMs);
|
|
36144
|
+
const hint = budgetOverrunHint(msg, scriptBudgetMs) ?? (reloadExpr === void 0 ? navigationAbortHint(msg) : null);
|
|
35855
36145
|
if (!hint) throw err;
|
|
35856
36146
|
throw new Error(`${msg}
|
|
35857
36147
|
${hint}`);
|
|
@@ -35883,7 +36173,12 @@ ${hint}`);
|
|
|
35883
36173
|
console.log(`${warning("\u26A0 Navigation incomplete:")} ${d.note || "page did not reach full load"}`);
|
|
35884
36174
|
}
|
|
35885
36175
|
if (d.slowRender) {
|
|
35886
|
-
|
|
36176
|
+
const slow = slowRenderMessage(d.slowRender.fps, {
|
|
36177
|
+
camera: !!camera,
|
|
36178
|
+
waitMs,
|
|
36179
|
+
script: [expr, ...steps, reloadExpr ?? ""].join("\n")
|
|
36180
|
+
});
|
|
36181
|
+
if (slow) console.log(slow);
|
|
35887
36182
|
}
|
|
35888
36183
|
if (d.auth?.requested) {
|
|
35889
36184
|
const who = getAuth()?.email;
|
|
@@ -35918,7 +36213,7 @@ ${bold("After reload")} ${muted("(page reloaded in place \u2014 storage preserve
|
|
|
35918
36213
|
} finally {
|
|
35919
36214
|
for (const h of [...hosted, ...camera ? [camera] : []]) {
|
|
35920
36215
|
try {
|
|
35921
|
-
await deleteFixture(
|
|
36216
|
+
await deleteFixture(projectGuid2, h.guid);
|
|
35922
36217
|
} catch (err) {
|
|
35923
36218
|
console.error(warning(`\u26A0 Could not auto-delete fixture "${h.name}" (${h.guid}) \u2014 still hosted at ${h.url}: ${err.message}`));
|
|
35924
36219
|
}
|
|
@@ -36217,14 +36512,14 @@ var pageScreenshotCommand = new Command("screenshot").description("Screenshot a
|
|
|
36217
36512
|
const ts2 = timestampSlug();
|
|
36218
36513
|
const shotName = (vp2) => defaultFilename(slug, ts2, vp2 && userSpecifiedViewports ? dimSuffix(vp2) : void 0);
|
|
36219
36514
|
const names = userSpecifiedViewports ? customViewports.map(shotName) : [shotName()];
|
|
36220
|
-
const
|
|
36221
|
-
const save = !opts.ephemeral &&
|
|
36515
|
+
const projectGuid2 = getConfig()?.projectGuid;
|
|
36516
|
+
const save = !opts.ephemeral && projectGuid2 ? { project_guid: projectGuid2, names } : void 0;
|
|
36222
36517
|
let camera;
|
|
36223
36518
|
if (opts.camera) {
|
|
36224
36519
|
assertCameraFile(opts.camera);
|
|
36225
|
-
if (!
|
|
36520
|
+
if (!projectGuid2) throw new Error("--camera needs a linked project (the frame is hosted for the browser to fetch) \u2014 run `gipity link` first.");
|
|
36226
36521
|
console.log(muted(`Hosting camera feed ${opts.camera}...`));
|
|
36227
|
-
camera = await uploadCameraFeed(
|
|
36522
|
+
camera = await uploadCameraFeed(projectGuid2, opts.camera);
|
|
36228
36523
|
}
|
|
36229
36524
|
const preCapture = [
|
|
36230
36525
|
opts.waitFor ? buildWaitForGate(opts.waitFor, waitForTimeoutMs) : "",
|
|
@@ -36255,7 +36550,7 @@ var pageScreenshotCommand = new Command("screenshot").description("Screenshot a
|
|
|
36255
36550
|
} finally {
|
|
36256
36551
|
if (camera) {
|
|
36257
36552
|
try {
|
|
36258
|
-
await deleteFixture(
|
|
36553
|
+
await deleteFixture(projectGuid2, camera.guid);
|
|
36259
36554
|
} catch (err) {
|
|
36260
36555
|
console.error(warning(`\u26A0 Could not auto-delete camera feed "${camera.name}" (${camera.guid}) \u2014 still hosted at ${camera.url}: ${err.message}`));
|
|
36261
36556
|
}
|
|
@@ -36757,10 +37052,10 @@ dbCommand.command("list").description("List databases").option("--all", "List da
|
|
|
36757
37052
|
grouped.get(key).push(db2);
|
|
36758
37053
|
}
|
|
36759
37054
|
let first = true;
|
|
36760
|
-
for (const [
|
|
37055
|
+
for (const [projectGuid2, dbs] of grouped) {
|
|
36761
37056
|
if (!first) console.log("");
|
|
36762
37057
|
first = false;
|
|
36763
|
-
const label2 = dbs[0].projectSlug || dbs[0].projectName ||
|
|
37058
|
+
const label2 = dbs[0].projectSlug || dbs[0].projectName || projectGuid2;
|
|
36764
37059
|
console.log(label2);
|
|
36765
37060
|
for (const db2 of dbs) {
|
|
36766
37061
|
console.log(`${db2.friendlyName}`);
|
|
@@ -39240,11 +39535,53 @@ var grokAdapter = {
|
|
|
39240
39535
|
installHint: "curl -fsSL https://grok.x.ai/install.sh | sh (or see docs.x.ai/grok-build)"
|
|
39241
39536
|
};
|
|
39242
39537
|
|
|
39538
|
+
// src/agents/agy.ts
|
|
39539
|
+
var MODEL_ID_TO_AGY_DISPLAY = {
|
|
39540
|
+
"gemini-3.5-flash": "Gemini 3.5 Flash (Medium)",
|
|
39541
|
+
"gemini-3.1-pro-preview": "Gemini 3.1 Pro (High)"
|
|
39542
|
+
};
|
|
39543
|
+
function translateModel(model) {
|
|
39544
|
+
return MODEL_ID_TO_AGY_DISPLAY[model] ?? model;
|
|
39545
|
+
}
|
|
39546
|
+
function projectArgs(resume) {
|
|
39547
|
+
return resume ? ["--conversation", resume] : ["--new-project"];
|
|
39548
|
+
}
|
|
39549
|
+
var agyAdapter = {
|
|
39550
|
+
key: "agy",
|
|
39551
|
+
source: "agy",
|
|
39552
|
+
displayName: "Antigravity",
|
|
39553
|
+
providerName: "Google",
|
|
39554
|
+
binary: "agy",
|
|
39555
|
+
buildInteractiveArgs({ resume, model }) {
|
|
39556
|
+
const args = [...projectArgs(resume)];
|
|
39557
|
+
if (model) args.push("--model", translateModel(model));
|
|
39558
|
+
return args;
|
|
39559
|
+
},
|
|
39560
|
+
buildHeadlessArgs({ message, resume, model, bypassApprovals }) {
|
|
39561
|
+
const args = ["-p", message, ...projectArgs(resume)];
|
|
39562
|
+
if (model) args.push("--model", translateModel(model));
|
|
39563
|
+
if (bypassApprovals) args.push("--dangerously-skip-permissions");
|
|
39564
|
+
return args;
|
|
39565
|
+
},
|
|
39566
|
+
sessionIdFromStreamEvent() {
|
|
39567
|
+
return null;
|
|
39568
|
+
},
|
|
39569
|
+
hooksSupportedOnPlatform(platform2) {
|
|
39570
|
+
return platform2 !== "win32";
|
|
39571
|
+
},
|
|
39572
|
+
// No stream-json ingest mapper for agy: hook capture carries the full
|
|
39573
|
+
// conversationId on every event (confirmed live, including headless -p),
|
|
39574
|
+
// so the daemon doesn't need to parse agy's stdout at all.
|
|
39575
|
+
daemonStreamCapture: false,
|
|
39576
|
+
installHint: "see https://antigravity.google (Google account sign-in required)"
|
|
39577
|
+
};
|
|
39578
|
+
|
|
39243
39579
|
// src/agents/index.ts
|
|
39244
39580
|
var AGENT_ADAPTERS = [
|
|
39245
39581
|
claudeCodeAdapter,
|
|
39246
39582
|
codexAdapter,
|
|
39247
|
-
grokAdapter
|
|
39583
|
+
grokAdapter,
|
|
39584
|
+
agyAdapter
|
|
39248
39585
|
];
|
|
39249
39586
|
var AGENT_KEYS = AGENT_ADAPTERS.map((a) => a.key);
|
|
39250
39587
|
function getAdapter(key) {
|
|
@@ -39316,10 +39653,10 @@ function reportSyncResult(result) {
|
|
|
39316
39653
|
console.log(` ${muted("Nothing was deleted. Re-run `gipity sync` to finish the pull.")}`);
|
|
39317
39654
|
}
|
|
39318
39655
|
}
|
|
39319
|
-
async function fetchProjectStats(
|
|
39320
|
-
if (
|
|
39656
|
+
async function fetchProjectStats(projectGuid2, cwd) {
|
|
39657
|
+
if (projectGuid2) {
|
|
39321
39658
|
try {
|
|
39322
|
-
const res = await get(`/projects/${encodeURIComponent(
|
|
39659
|
+
const res = await get(`/projects/${encodeURIComponent(projectGuid2)}/files/stats`);
|
|
39323
39660
|
const d = res.data;
|
|
39324
39661
|
const topLevelNames = d.top_level.map((e) => e.type === "directory" ? `${e.name}/` : e.name);
|
|
39325
39662
|
return {
|
|
@@ -39913,7 +40250,7 @@ async function pickAgent(lastUsed) {
|
|
|
39913
40250
|
AGENT_ADAPTERS.forEach((a, i) => {
|
|
39914
40251
|
const notes = [];
|
|
39915
40252
|
if (a.key === lastUsed) notes.push("last used");
|
|
39916
|
-
if (a.key === "codex" && !a.hooksSupportedOnPlatform(process.platform)) {
|
|
40253
|
+
if ((a.key === "codex" || a.key === "agy") && !a.hooksSupportedOnPlatform(process.platform)) {
|
|
39917
40254
|
notes.push("session recording unavailable on Windows");
|
|
39918
40255
|
}
|
|
39919
40256
|
const note = notes.length ? ` ${muted(`(${notes.join(", ")})`)}` : "";
|
|
@@ -40490,6 +40827,87 @@ Pulled ${syncResult.applied} files to local.`);
|
|
|
40490
40827
|
}
|
|
40491
40828
|
}));
|
|
40492
40829
|
|
|
40830
|
+
// src/commands/brand.ts
|
|
40831
|
+
init_api();
|
|
40832
|
+
init_config();
|
|
40833
|
+
init_colors();
|
|
40834
|
+
function printBrand(d) {
|
|
40835
|
+
console.log(`${bold("Glyph:")} ${d.resolved.glyph}${d.resolved.isEmoji ? muted(" (emoji)") : ""}`);
|
|
40836
|
+
console.log(`${bold("Accent:")} ${d.resolved.accent}${d.branding.primary_color ? "" : muted(" (derived from project)")}`);
|
|
40837
|
+
if (d.branding.app_name) console.log(`${bold("Name:")} ${d.branding.app_name}`);
|
|
40838
|
+
if (d.branding.tagline) console.log(`${bold("Tagline:")} ${d.branding.tagline}`);
|
|
40839
|
+
}
|
|
40840
|
+
var DEPLOY_HINT = "Deploy to publish the new assets: gipity deploy dev";
|
|
40841
|
+
var brandCommand = new Command("brand").description("The app's generated icons + share card (favicons, iOS icon, og-image)").addHelpText("after", `
|
|
40842
|
+
Examples:
|
|
40843
|
+
gipity brand Show the current brand
|
|
40844
|
+
gipity brand set --emoji \u{1F98D} Emoji icon + share card, regenerate all assets
|
|
40845
|
+
gipity brand set --glyph R --color "#3b82f6" Letter glyph on a blue accent
|
|
40846
|
+
gipity brand set --tagline "Bananas at 60fps" Subtitle on the share card
|
|
40847
|
+
gipity brand apply Re-render assets from the stored brand
|
|
40848
|
+
gipity brand apply --fix-head Migrate an older app: assets + splice the
|
|
40849
|
+
share/SEO tags into src/index.html
|
|
40850
|
+
|
|
40851
|
+
Assets are generated server-side into src/images/ (+ src/manifest.webmanifest)
|
|
40852
|
+
and are versioned like any other file - rollback restores previous ones.`);
|
|
40853
|
+
brandCommand.command("show", { isDefault: true }).description("Show the stored brand and the resolved render spec").option("--project <guid-or-slug>", "Target a specific project instead of cwd / Home").option("--json", "Output raw JSON").action((opts) => run("Brand", async () => {
|
|
40854
|
+
const { config } = await resolveProjectContext({ projectOverride: opts.project });
|
|
40855
|
+
const res = await get(`/projects/${config.projectGuid}/brand`);
|
|
40856
|
+
if (opts.json) {
|
|
40857
|
+
console.log(JSON.stringify(res.data));
|
|
40858
|
+
return;
|
|
40859
|
+
}
|
|
40860
|
+
printBrand(res.data);
|
|
40861
|
+
console.log(muted(`
|
|
40862
|
+
Assets: ${(res.data.assets ?? []).join(", ")}`));
|
|
40863
|
+
console.log(muted(`Change it: gipity brand set --emoji \u{1F3A8} | --glyph A | --color "#RRGGBB" | --tagline "..."`));
|
|
40864
|
+
}));
|
|
40865
|
+
brandCommand.command("set").description("Change brand fields and regenerate all assets").option("--emoji <emoji>", "Emoji glyph for the icon + share card (e.g. \u{1F98D})").option("--glyph <char>", "Letter/digit glyph (alias of --emoji, any single character)").option("--color <hex>", "Accent color, #RRGGBB").option("--name <name>", "Display name override (og/share title stays the page title)").option("--tagline <text>", "One-line subtitle on the share card (max 120 chars)").option("--fix-head", "Also splice the share/SEO head block into src/index.html (for apps installed before templates shipped it)").option("--no-regenerate", "Store the fields without re-rendering assets").option("--project <guid-or-slug>", "Target a specific project instead of cwd / Home").option("--json", "Output raw JSON").action((opts) => run("Brand", async () => {
|
|
40866
|
+
const { config } = await resolveProjectContext({ projectOverride: opts.project });
|
|
40867
|
+
const body = {};
|
|
40868
|
+
const glyph = opts.emoji ?? opts.glyph;
|
|
40869
|
+
if (glyph) body.icon_glyph = glyph;
|
|
40870
|
+
if (opts.color) body.primary_color = opts.color;
|
|
40871
|
+
if (opts.name) body.app_name = opts.name;
|
|
40872
|
+
if (opts.tagline) body.tagline = opts.tagline;
|
|
40873
|
+
if (opts.regenerate === false) body.regenerate = false;
|
|
40874
|
+
if (opts.fixHead) body.fix_head = true;
|
|
40875
|
+
if (!glyph && !opts.color && !opts.name && !opts.tagline) {
|
|
40876
|
+
console.error("Nothing to set - pass --emoji, --glyph, --color, --name, or --tagline. (gipity brand set --help; use gipity brand apply to just re-render)");
|
|
40877
|
+
process.exitCode = 1;
|
|
40878
|
+
return;
|
|
40879
|
+
}
|
|
40880
|
+
const res = await post(`/projects/${config.projectGuid}/brand`, body);
|
|
40881
|
+
if (opts.json) {
|
|
40882
|
+
console.log(JSON.stringify(res.data));
|
|
40883
|
+
return;
|
|
40884
|
+
}
|
|
40885
|
+
printBrand(res.data);
|
|
40886
|
+
reportFiles(res.data.files ?? []);
|
|
40887
|
+
}));
|
|
40888
|
+
brandCommand.command("apply").description("Re-render all brand assets from the stored brand (no field changes)").option("--fix-head", "Also splice the share/SEO head block into src/index.html (for apps installed before templates shipped it)").option("--project <guid-or-slug>", "Target a specific project instead of cwd / Home").option("--json", "Output raw JSON").action((opts) => run("Brand", async () => {
|
|
40889
|
+
const { config } = await resolveProjectContext({ projectOverride: opts.project });
|
|
40890
|
+
const body = opts.fixHead ? { fix_head: true } : {};
|
|
40891
|
+
const res = await post(`/projects/${config.projectGuid}/brand`, body);
|
|
40892
|
+
if (opts.json) {
|
|
40893
|
+
console.log(JSON.stringify(res.data));
|
|
40894
|
+
return;
|
|
40895
|
+
}
|
|
40896
|
+
reportFiles(res.data.files ?? []);
|
|
40897
|
+
}));
|
|
40898
|
+
function reportFiles(files) {
|
|
40899
|
+
if (!files.length) {
|
|
40900
|
+
console.log(muted("\nStored (assets not regenerated - run gipity brand apply)."));
|
|
40901
|
+
return;
|
|
40902
|
+
}
|
|
40903
|
+
console.log(success(`
|
|
40904
|
+
\u2713 Regenerated ${files.length} asset${files.length === 1 ? "" : "s"}.`));
|
|
40905
|
+
if (files.includes("src/index.html")) {
|
|
40906
|
+
console.log(success("\u2713 src/index.html head updated (page title/description preserved)."));
|
|
40907
|
+
}
|
|
40908
|
+
console.log(muted(` ${DEPLOY_HINT}`));
|
|
40909
|
+
}
|
|
40910
|
+
|
|
40493
40911
|
// src/commands/remove.ts
|
|
40494
40912
|
init_api();
|
|
40495
40913
|
init_config();
|
|
@@ -41262,10 +41680,31 @@ pageCommand.action(() => {
|
|
|
41262
41680
|
|
|
41263
41681
|
// src/commands/records.ts
|
|
41264
41682
|
init_api();
|
|
41683
|
+
init_auth();
|
|
41265
41684
|
init_config();
|
|
41266
41685
|
init_colors();
|
|
41267
41686
|
init_utils();
|
|
41268
41687
|
var recordsCommand = new Command("records").description("Manage app records (Gipity Records - validated CRUD with audit history)");
|
|
41688
|
+
var ANON_FLAG = "--anon";
|
|
41689
|
+
var ANON_HELP = "Call as an anonymous visitor (the public path a signed-out user hits) instead of as your signed-in account";
|
|
41690
|
+
async function recordsHttp(opts) {
|
|
41691
|
+
const config = requireConfig();
|
|
41692
|
+
if (!opts.anon) {
|
|
41693
|
+
const who = getAuth()?.email;
|
|
41694
|
+
console.error(muted(`Auth: calling as ${who ?? "your signed-in account"} (the owner persona; use ${ANON_FLAG} for the public visitor path)`));
|
|
41695
|
+
return { guid: config.projectGuid, get, post, put, patch, del };
|
|
41696
|
+
}
|
|
41697
|
+
console.error(muted("Auth: anonymous visitor (the public path a signed-out user hits)"));
|
|
41698
|
+
const headers = await mintAppToken(config.projectGuid);
|
|
41699
|
+
return {
|
|
41700
|
+
guid: config.projectGuid,
|
|
41701
|
+
get: (path5) => publicRequest("GET", path5, void 0, headers),
|
|
41702
|
+
post: (path5, body) => publicRequest("POST", path5, body, headers),
|
|
41703
|
+
put: (path5, body) => publicRequest("PUT", path5, body, headers),
|
|
41704
|
+
patch: (path5, body) => publicRequest("PATCH", path5, body, headers),
|
|
41705
|
+
del: (path5, body) => publicRequest("DELETE", path5, body, headers)
|
|
41706
|
+
};
|
|
41707
|
+
}
|
|
41269
41708
|
recordsCommand.command("list").description("List record tables").option("--json", "Output as JSON").action((opts) => run("List", async () => {
|
|
41270
41709
|
const config = requireConfig();
|
|
41271
41710
|
const res = await get(`/api/${config.projectGuid}/records-config`);
|
|
@@ -41297,16 +41736,16 @@ recordsCommand.command("config <table>").description("Show or set a table's Reco
|
|
|
41297
41736
|
res.data
|
|
41298
41737
|
);
|
|
41299
41738
|
}));
|
|
41300
|
-
recordsCommand.command("query <table>").description("List records").option("--filter <filter>", 'Filter string (e.g., "status:eq:active")').option("--sort <sort>", 'Sort string (e.g., "created_at:desc")').option("--limit <n>", "Max rows", "20").option("--offset <n>", "Offset", "0").option("--fields <fields>", "Comma-separated column names").option("--json", "Output as JSON").action((table, opts) => run("Query", async () => {
|
|
41301
|
-
const
|
|
41739
|
+
recordsCommand.command("query <table>").description("List records").option("--filter <filter>", 'Filter string (e.g., "status:eq:active")').option("--sort <sort>", 'Sort string (e.g., "created_at:desc")').option("--limit <n>", "Max rows", "20").option("--offset <n>", "Offset", "0").option("--fields <fields>", "Comma-separated column names").option(ANON_FLAG, ANON_HELP).option("--json", "Output as JSON").action((table, opts) => run("Query", async () => {
|
|
41740
|
+
const api = await recordsHttp(opts);
|
|
41302
41741
|
const params = new URLSearchParams();
|
|
41303
41742
|
if (opts.filter) params.set("filter", opts.filter);
|
|
41304
41743
|
if (opts.sort) params.set("sort", opts.sort);
|
|
41305
41744
|
params.set("limit", opts.limit);
|
|
41306
41745
|
params.set("offset", opts.offset);
|
|
41307
41746
|
if (opts.fields) params.set("fields", opts.fields);
|
|
41308
|
-
const res = await get(
|
|
41309
|
-
`/api/${
|
|
41747
|
+
const res = await api.get(
|
|
41748
|
+
`/api/${api.guid}/records/${table}?${params}`
|
|
41310
41749
|
);
|
|
41311
41750
|
if (opts.json) {
|
|
41312
41751
|
console.log(JSON.stringify(res));
|
|
@@ -41319,41 +41758,42 @@ recordsCommand.command("query <table>").description("List records").option("--fi
|
|
|
41319
41758
|
console.log("");
|
|
41320
41759
|
}
|
|
41321
41760
|
}));
|
|
41322
|
-
recordsCommand.command("get <table> <id>").description("Get a record").option("--json", "Output as JSON").action((table, id2, opts) => run("Get", async () => {
|
|
41323
|
-
const
|
|
41324
|
-
const res = await get(`/api/${
|
|
41761
|
+
recordsCommand.command("get <table> <id>").description("Get a record").option(ANON_FLAG, ANON_HELP).option("--json", "Output as JSON").action((table, id2, opts) => run("Get", async () => {
|
|
41762
|
+
const api = await recordsHttp(opts);
|
|
41763
|
+
const res = await api.get(`/api/${api.guid}/records/${table}/${id2}`);
|
|
41325
41764
|
console.log(opts.json ? JSON.stringify(res.data) : JSON.stringify(res.data, null, 2));
|
|
41326
41765
|
}));
|
|
41327
|
-
recordsCommand.command("history <table>
|
|
41328
|
-
const
|
|
41329
|
-
const
|
|
41330
|
-
|
|
41766
|
+
recordsCommand.command("history <table> [id]").description("Audit history (who/what changed it, with English summaries). Omit <id> for the whole table's feed.").option("--limit <n>", "Max events", "20").option(ANON_FLAG, ANON_HELP).option("--json", "Output as JSON").action((table, id2, opts) => run("History", async () => {
|
|
41767
|
+
const api = await recordsHttp(opts);
|
|
41768
|
+
const scope = id2 ? `${table}/${encodeURIComponent(id2)}` : table;
|
|
41769
|
+
const res = await api.get(
|
|
41770
|
+
`/api/${api.guid}/records/${scope}/history?limit=${encodeURIComponent(opts.limit)}`
|
|
41331
41771
|
);
|
|
41332
|
-
printList(res.data, opts, "No history for this record."
|
|
41772
|
+
printList(res.data, opts, id2 ? "No history for this record." : `No history for "${table}".`, (e) => {
|
|
41333
41773
|
const summary = e.detail?.summary || `${e.action} ${e.entity_type} ${e.entity_id}`;
|
|
41334
41774
|
return `${muted(e.created_at)} ${bold(e.source || "-")} ${summary}`;
|
|
41335
41775
|
});
|
|
41336
41776
|
}));
|
|
41337
|
-
recordsCommand.command("create <table>").description("Create a record").requiredOption("--data <json>", "JSON object with field values").option("--json", "Output as JSON").action((table, opts) => run("Create", async () => {
|
|
41338
|
-
const
|
|
41777
|
+
recordsCommand.command("create <table>").description("Create a record").requiredOption("--data <json>", "JSON object with field values").option(ANON_FLAG, ANON_HELP).option("--json", "Output as JSON").action((table, opts) => run("Create", async () => {
|
|
41778
|
+
const api = await recordsHttp(opts);
|
|
41339
41779
|
const data = JSON.parse(opts.data);
|
|
41340
|
-
const res = await post(`/api/${
|
|
41780
|
+
const res = await api.post(`/api/${api.guid}/records/${table}`, data);
|
|
41341
41781
|
printResult(`Created: ${JSON.stringify(res.data)}`, opts, res.data);
|
|
41342
41782
|
}));
|
|
41343
|
-
recordsCommand.command("update <table> <id>").description("Update a record").requiredOption("--data <json>", "JSON object with fields to update").option("--json", "Output as JSON").action((table, id2, opts) => run("Update", async () => {
|
|
41344
|
-
const
|
|
41783
|
+
recordsCommand.command("update <table> <id>").description("Update a record").requiredOption("--data <json>", "JSON object with fields to update").option(ANON_FLAG, ANON_HELP).option("--json", "Output as JSON").action((table, id2, opts) => run("Update", async () => {
|
|
41784
|
+
const api = await recordsHttp(opts);
|
|
41345
41785
|
const data = JSON.parse(opts.data);
|
|
41346
|
-
const res = await put(`/api/${
|
|
41786
|
+
const res = await api.put(`/api/${api.guid}/records/${table}/${id2}`, data);
|
|
41347
41787
|
printResult(`Updated: ${JSON.stringify(res.data)}`, opts, res.data);
|
|
41348
41788
|
}));
|
|
41349
|
-
recordsCommand.command("delete <table> <id>").description("Delete a record").action((table, id2) => run("Delete", async () => {
|
|
41789
|
+
recordsCommand.command("delete <table> <id>").description("Delete a record").option(ANON_FLAG, ANON_HELP).option("--json", "Output as JSON").action((table, id2, opts) => run("Delete", async () => {
|
|
41350
41790
|
if (!await confirm(`Delete record ${id2} from "${table}"?`)) {
|
|
41351
|
-
|
|
41791
|
+
printResult("Cancelled.", opts, { table, id: id2, deleted: false, cancelled: true });
|
|
41352
41792
|
return;
|
|
41353
41793
|
}
|
|
41354
|
-
const
|
|
41355
|
-
await del(`/api/${
|
|
41356
|
-
printResult("Deleted.", {
|
|
41794
|
+
const api = await recordsHttp(opts);
|
|
41795
|
+
const res = await api.del(`/api/${api.guid}/records/${table}/${id2}`);
|
|
41796
|
+
printResult("Deleted.", opts, res?.data ?? { table, id: id2, deleted: true });
|
|
41357
41797
|
}));
|
|
41358
41798
|
|
|
41359
41799
|
// src/commands/fn.ts
|
|
@@ -41398,17 +41838,11 @@ ${error(`error: ${log3.error_message}`)}`;
|
|
|
41398
41838
|
return line;
|
|
41399
41839
|
});
|
|
41400
41840
|
}));
|
|
41401
|
-
async function callAnon(
|
|
41402
|
-
let appToken;
|
|
41403
|
-
try {
|
|
41404
|
-
const minted = await publicPost("/api/token", { app: projectGuid });
|
|
41405
|
-
appToken = minted.data.token;
|
|
41406
|
-
} catch {
|
|
41407
|
-
}
|
|
41841
|
+
async function callAnon(projectGuid2, name, body) {
|
|
41408
41842
|
return publicPost(
|
|
41409
|
-
`/api/${
|
|
41843
|
+
`/api/${projectGuid2}/fn/${encodeURIComponent(name)}`,
|
|
41410
41844
|
body,
|
|
41411
|
-
|
|
41845
|
+
await mintAppToken(projectGuid2)
|
|
41412
41846
|
);
|
|
41413
41847
|
}
|
|
41414
41848
|
fnCommand.command("call <name> [body]").description("Call a function").option("-d, --data <json>", "JSON request body: inline JSON, @file to read a file, or @- / - for stdin").option("--file <field=@path>", "Attach a file as { data, media_type } under <field> (the vision/media service shape), repeatable, e.g. --file image=@receipt.png", (v7, acc) => (acc || []).concat(v7)).option("--anon", "Call as an anonymous visitor (the public path a signed-out user hits) instead of as your signed-in account").option("--field <path>", "Print only this field of the result (dot path, e.g. items.0.short_guid)").option("--json", "Output as JSON").action((name, bodyArg, opts) => run("Call", async () => {
|
|
@@ -42845,7 +43279,7 @@ function undeployedFunctionsFromFailures(results) {
|
|
|
42845
43279
|
return [...names];
|
|
42846
43280
|
}
|
|
42847
43281
|
var LONG_RUN_MS = 6e4;
|
|
42848
|
-
async function pollTestStatus(
|
|
43282
|
+
async function pollTestStatus(projectGuid2, runGuid, opts) {
|
|
42849
43283
|
const startTime = Date.now();
|
|
42850
43284
|
const getPollInterval = () => {
|
|
42851
43285
|
const elapsed = Date.now() - startTime;
|
|
@@ -42867,7 +43301,7 @@ async function pollTestStatus(projectGuid, runGuid, opts) {
|
|
|
42867
43301
|
if (Date.now() - startTime > POLL_HARD_CAP_MS) {
|
|
42868
43302
|
throw new Error(`Test run ${runGuid} still not finished after ${Math.round(POLL_HARD_CAP_MS / 6e4)} minutes - giving up on the poll. Check it later with \`gipity test status ${runGuid}\` or re-run.`);
|
|
42869
43303
|
}
|
|
42870
|
-
const res = await get(`/projects/${
|
|
43304
|
+
const res = await get(`/projects/${projectGuid2}/test/status/${runGuid}`);
|
|
42871
43305
|
const data = res.data;
|
|
42872
43306
|
if (!opts.json && data.results.length > lastResultCount) {
|
|
42873
43307
|
const newResults = data.results.slice(lastResultCount);
|
|
@@ -44388,10 +44822,11 @@ async function collectDiagnostics() {
|
|
|
44388
44822
|
return [];
|
|
44389
44823
|
}
|
|
44390
44824
|
})();
|
|
44391
|
-
const [claude, codex, grok, cursor, gpu] = await Promise.all([
|
|
44825
|
+
const [claude, codex, grok, agy, cursor, gpu] = await Promise.all([
|
|
44392
44826
|
probeVersion("claude"),
|
|
44393
44827
|
probeVersion("codex"),
|
|
44394
44828
|
probeVersion("grok"),
|
|
44829
|
+
probeVersion("agy"),
|
|
44395
44830
|
probeVersion("cursor"),
|
|
44396
44831
|
detectGpu()
|
|
44397
44832
|
]);
|
|
@@ -44399,6 +44834,7 @@ async function collectDiagnostics() {
|
|
|
44399
44834
|
if (claude) agents.claude_code = claude;
|
|
44400
44835
|
if (codex) agents.codex = codex;
|
|
44401
44836
|
if (grok) agents.grok = grok;
|
|
44837
|
+
if (agy) agents.agy = agy;
|
|
44402
44838
|
if (cursor) agents.cursor = cursor;
|
|
44403
44839
|
return {
|
|
44404
44840
|
collected_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -46619,6 +47055,16 @@ var uninstallCommand = new Command("uninstall").description("Uninstall Gipity").
|
|
|
46619
47055
|
}
|
|
46620
47056
|
console.log(`${success(`Removed ${agentSkills.skills.length} Gipity skills from ~/.agents/skills.`)}`);
|
|
46621
47057
|
}
|
|
47058
|
+
const agySkills = agySkillsState();
|
|
47059
|
+
if (agySkills.skills.length) {
|
|
47060
|
+
for (const name of agySkills.skills) {
|
|
47061
|
+
try {
|
|
47062
|
+
rmSync4(join25(AGY_SKILLS_DIR, name), { recursive: true, force: true });
|
|
47063
|
+
} catch {
|
|
47064
|
+
}
|
|
47065
|
+
}
|
|
47066
|
+
console.log(`${success(`Removed ${agySkills.skills.length} Gipity skills from ~/.gemini/config/skills.`)}`);
|
|
47067
|
+
}
|
|
46622
47068
|
if (existsSync23(gipityDir)) {
|
|
46623
47069
|
try {
|
|
46624
47070
|
rmSync4(gipityDir, { recursive: true, force: true });
|
|
@@ -47238,8 +47684,8 @@ function configureHelp(cmd) {
|
|
|
47238
47684
|
var program2 = new Command();
|
|
47239
47685
|
program2.enablePositionalOptions();
|
|
47240
47686
|
var startGroup = [statusCommand, initCommand, skillCommand, projectCommand];
|
|
47241
|
-
var buildGroup = [addCommand, removeCommand, saveCommand, loadCommand, deployCommand, pageCommand, testCommand];
|
|
47242
|
-
var backendGroup = [dbCommand, fnCommand, secretsCommand, logsCommand, jobCommand, workflowCommand];
|
|
47687
|
+
var buildGroup = [addCommand, removeCommand, brandCommand, saveCommand, loadCommand, deployCommand, pageCommand, testCommand];
|
|
47688
|
+
var backendGroup = [dbCommand, fnCommand, secretsCommand, keyCommand, logsCommand, jobCommand, workflowCommand];
|
|
47243
47689
|
var servicesGroup = [serviceCommand, generateCommand, notifyCommand, paymentsCommand, realtimeCommand, recordsCommand, rbacCommand, auditCommand, domainCommand, tokenCommand];
|
|
47244
47690
|
var filesGroup = [syncCommand, fileCommand, pushCommand, uploadCommand, storageCommand];
|
|
47245
47691
|
var gipGroup = [chatCommand, memoryCommand, agentCommand, approvalCommand, gmailCommand];
|
|
@@ -47256,6 +47702,7 @@ var HELP_SECTIONS = [
|
|
|
47256
47702
|
{ title: "Connect & setup", cmds: connectGroup }
|
|
47257
47703
|
];
|
|
47258
47704
|
var SKILL_DOCS = {
|
|
47705
|
+
brand: "web-app-basics",
|
|
47259
47706
|
save: "app-import",
|
|
47260
47707
|
load: "app-import",
|
|
47261
47708
|
github: "app-import",
|
|
@@ -47268,6 +47715,7 @@ var SKILL_DOCS = {
|
|
|
47268
47715
|
notify: "app-notify",
|
|
47269
47716
|
payments: "app-payments",
|
|
47270
47717
|
records: "app-records",
|
|
47718
|
+
key: "app-auth",
|
|
47271
47719
|
realtime: "app-realtime",
|
|
47272
47720
|
job: "jobs",
|
|
47273
47721
|
sandbox: "sandbox-tools",
|