gipity 1.0.130 → 1.0.228
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -13
- package/dist/__tests__/claude-noninteractive.test.d.ts +1 -0
- package/dist/__tests__/claude-noninteractive.test.js +65 -0
- package/dist/__tests__/claude-noninteractive.test.js.map +1 -0
- package/dist/__tests__/cli-e2e-live.test.d.ts +1 -0
- package/dist/__tests__/cli-e2e-live.test.js +144 -0
- package/dist/__tests__/cli-e2e-live.test.js.map +1 -0
- package/dist/__tests__/cli-smoke.test.d.ts +1 -0
- package/dist/__tests__/cli-smoke.test.js +64 -0
- package/dist/__tests__/cli-smoke.test.js.map +1 -0
- package/dist/__tests__/flag-aliases.test.d.ts +1 -0
- package/dist/__tests__/flag-aliases.test.js +33 -0
- package/dist/__tests__/flag-aliases.test.js.map +1 -0
- package/dist/__tests__/helpers/spawn-cli.d.ts +30 -0
- package/dist/__tests__/helpers/spawn-cli.js +72 -0
- package/dist/__tests__/helpers/spawn-cli.js.map +1 -0
- package/dist/__tests__/hook-capture.test.d.ts +1 -0
- package/dist/__tests__/hook-capture.test.js +65 -0
- package/dist/__tests__/hook-capture.test.js.map +1 -0
- package/dist/__tests__/relay-bridge-abort.test.d.ts +1 -0
- package/dist/__tests__/relay-bridge-abort.test.js +65 -0
- package/dist/__tests__/relay-bridge-abort.test.js.map +1 -0
- package/dist/__tests__/relay-daemon.test.d.ts +1 -0
- package/dist/__tests__/relay-daemon.test.js +228 -0
- package/dist/__tests__/relay-daemon.test.js.map +1 -0
- package/dist/__tests__/relay-installers.test.d.ts +1 -0
- package/dist/__tests__/relay-installers.test.js +69 -0
- package/dist/__tests__/relay-installers.test.js.map +1 -0
- package/dist/__tests__/relay-pair.test.d.ts +1 -0
- package/dist/__tests__/relay-pair.test.js.map +1 -0
- package/dist/__tests__/relay-state.test.d.ts +1 -0
- package/dist/__tests__/relay-state.test.js +85 -0
- package/dist/__tests__/relay-state.test.js.map +1 -0
- package/dist/__tests__/sync.test.js +59 -0
- package/dist/__tests__/sync.test.js.map +1 -1
- package/dist/__tests__/updater.test.d.ts +1 -0
- package/dist/__tests__/updater.test.js +95 -0
- package/dist/__tests__/updater.test.js.map +1 -0
- package/dist/__tests__/upload.test.d.ts +1 -0
- package/dist/__tests__/upload.test.js +92 -0
- package/dist/__tests__/upload.test.js.map +1 -0
- package/dist/api.d.ts +8 -0
- package/dist/api.js +38 -0
- package/dist/api.js.map +1 -1
- package/dist/banner.js +2 -2
- package/dist/banner.js.map +1 -1
- package/dist/commands/agent.js +1 -1
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/browser.js +2 -2
- package/dist/commands/browser.js.map +1 -1
- package/dist/commands/chat.js +2 -2
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/claude.js +324 -93
- package/dist/commands/claude.js.map +1 -1
- package/dist/commands/db.js +1 -1
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/deploy.js +8 -0
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +67 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/email.js +2 -2
- package/dist/commands/email.js.map +1 -1
- package/dist/commands/file.js +8 -8
- package/dist/commands/file.js.map +1 -1
- package/dist/commands/fn.js +5 -4
- package/dist/commands/fn.js.map +1 -1
- package/dist/commands/generate.js +4 -4
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/hook-capture.d.ts +15 -0
- package/dist/commands/hook-capture.js +215 -0
- package/dist/commands/hook-capture.js.map +1 -0
- package/dist/commands/init.js +3 -6
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/location.d.ts +9 -0
- package/dist/commands/location.js +79 -0
- package/dist/commands/location.js.map +1 -0
- package/dist/commands/memory.js +6 -6
- package/dist/commands/memory.js.map +1 -1
- package/dist/commands/project.js +21 -2
- package/dist/commands/project.js.map +1 -1
- package/dist/commands/rbac.js +1 -1
- package/dist/commands/rbac.js.map +1 -1
- package/dist/commands/records.js +1 -1
- package/dist/commands/records.js.map +1 -1
- package/dist/commands/relay-install.d.ts +11 -0
- package/dist/commands/relay-install.js +104 -0
- package/dist/commands/relay-install.js.map +1 -0
- package/dist/commands/relay.d.ts +9 -0
- package/dist/commands/relay.js +234 -0
- package/dist/commands/relay.js.map +1 -0
- package/dist/commands/sandbox.js +7 -2
- package/dist/commands/sandbox.js.map +1 -1
- package/dist/commands/scaffold.js +9 -1
- package/dist/commands/scaffold.js.map +1 -1
- package/dist/commands/skills.js +3 -3
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/status.js +53 -2
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/test.js +22 -7
- package/dist/commands/test.js.map +1 -1
- package/dist/commands/uninstall.d.ts +10 -0
- package/dist/commands/uninstall.js +161 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.js +20 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/upload.d.ts +2 -0
- package/dist/commands/upload.js +131 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.js +68 -0
- package/dist/config.js.map +1 -1
- package/dist/flag-aliases.d.ts +12 -0
- package/dist/flag-aliases.js +44 -0
- package/dist/flag-aliases.js.map +1 -0
- package/dist/index.js +76 -14
- package/dist/index.js.map +1 -1
- package/dist/relay/daemon.d.ts +23 -0
- package/dist/relay/daemon.js +485 -0
- package/dist/relay/daemon.js.map +1 -0
- package/dist/relay/installers.d.ts +18 -0
- package/dist/relay/installers.js +142 -0
- package/dist/relay/installers.js.map +1 -0
- package/dist/relay/onboarding.d.ts +8 -0
- package/dist/relay/onboarding.js +130 -0
- package/dist/relay/onboarding.js.map +1 -0
- package/dist/relay/paths.d.ts +5 -0
- package/dist/relay/paths.js +30 -0
- package/dist/relay/paths.js.map +1 -0
- package/dist/relay/state.d.ts +39 -0
- package/dist/relay/state.js +135 -0
- package/dist/relay/state.js.map +1 -0
- package/dist/setup.d.ts +36 -1
- package/dist/setup.js +190 -12
- package/dist/setup.js.map +1 -1
- package/dist/sync.d.ts +13 -6
- package/dist/sync.js +218 -58
- package/dist/sync.js.map +1 -1
- package/dist/updater/bootstrap.d.ts +8 -0
- package/dist/updater/bootstrap.js +45 -0
- package/dist/updater/bootstrap.js.map +1 -0
- package/dist/updater/check.d.ts +12 -0
- package/dist/updater/check.js +96 -0
- package/dist/updater/check.js.map +1 -0
- package/dist/updater/shim.d.ts +2 -0
- package/dist/updater/shim.js +50 -0
- package/dist/updater/shim.js.map +1 -0
- package/dist/updater/state.d.ts +24 -0
- package/dist/updater/state.js +60 -0
- package/dist/updater/state.js.map +1 -0
- package/dist/upload.d.ts +29 -0
- package/dist/upload.js +158 -0
- package/dist/upload.js.map +1 -0
- package/dist/utils.d.ts +23 -4
- package/dist/utils.js +60 -8
- package/dist/utils.js.map +1 -1
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -30,6 +30,17 @@ gipity claude
|
|
|
30
30
|
|
|
31
31
|
That's it. `claude` walks you through login, project setup, and launches Claude Code.
|
|
32
32
|
|
|
33
|
+
## Updates
|
|
34
|
+
|
|
35
|
+
The CLI auto-updates in the background. After your one-time `npm install -g gipity`, every run silently checks npm for a new version and installs it into `~/.gipity/local/` — no sudo, no re-running install commands. The new version takes effect on your next invocation.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
gipity doctor # show install version, last update check, opt-out status
|
|
39
|
+
gipity update # force an immediate update now
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
To opt out: `export DISABLE_AUTOUPDATER=1` (matches Claude Code), or set `{ "autoUpdates": false }` in `~/.gipity/settings.json`. CI environments are auto-detected and skipped.
|
|
43
|
+
|
|
33
44
|
## Quick Start
|
|
34
45
|
|
|
35
46
|
One command. It walks you through login, project setup, and drops you into Claude Code.
|
|
@@ -122,20 +133,24 @@ gipity sync down # Pull remote changes
|
|
|
122
133
|
| `gipity db` | Query, list, create, or drop project databases |
|
|
123
134
|
| `gipity memory` | Read/write agent and project memory |
|
|
124
135
|
| `gipity sandbox run <code>` | Execute code in a sandboxed container |
|
|
125
|
-
| `gipity api` | Define and manage API procedures |
|
|
126
136
|
| `gipity project` | List, create, switch, or delete projects |
|
|
127
137
|
| `gipity agent` | List, create, switch, or configure agents |
|
|
128
138
|
| `gipity workflow` | Manage and trigger automated workflows |
|
|
129
139
|
| `gipity file` | Browse remote files (ls, cat, tree) |
|
|
130
140
|
| `gipity scaffold [title]` | Create app structure (`--type web`, `--type 2d-game`, or `--type 3d-world`) |
|
|
131
|
-
| `gipity
|
|
141
|
+
| `gipity test` | Run project tests in sandboxed containers |
|
|
132
142
|
| `gipity logs fn <name>` | View function execution logs |
|
|
133
143
|
| `gipity browser <url>` | Inspect a URL: console errors, performance, failed resources |
|
|
134
144
|
| `gipity records` | Query and manage Records API tables |
|
|
135
|
-
| `gipity fn` | Manage and call
|
|
145
|
+
| `gipity fn` | Manage and call serverless functions |
|
|
136
146
|
| `gipity rbac` | Manage RBAC policies |
|
|
137
147
|
| `gipity audit` | Query audit logs |
|
|
138
148
|
| `gipity credits` | Check your balance and usage |
|
|
149
|
+
| `gipity skills` | List and manage agent skills |
|
|
150
|
+
| `gipity domain` | Manage custom domains for deployed apps |
|
|
151
|
+
| `gipity email` | Send emails via your agent |
|
|
152
|
+
| `gipity generate` | Generate images, audio, or video via your agent |
|
|
153
|
+
| `gipity logout` | Sign out and clear local tokens |
|
|
139
154
|
|
|
140
155
|
Every command supports `--json` for scripted/programmatic use.
|
|
141
156
|
|
|
@@ -189,16 +204,6 @@ gipity sandbox run "import pandas; print(pandas.__version__)" --lang py
|
|
|
189
204
|
gipity sandbox run "echo hello" --lang bash
|
|
190
205
|
```
|
|
191
206
|
|
|
192
|
-
### api
|
|
193
|
-
|
|
194
|
-
Define SQL-backed API endpoints that your deployed apps can call.
|
|
195
|
-
|
|
196
|
-
```bash
|
|
197
|
-
gipity api list
|
|
198
|
-
gipity api define get_users --sql "SELECT * FROM users" --database mydb --auth public
|
|
199
|
-
gipity api define create_post --sql @queries/create_post.sql --database mydb --method write
|
|
200
|
-
```
|
|
201
|
-
|
|
202
207
|
### workflow
|
|
203
208
|
|
|
204
209
|
```bash
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `gipity claude -p "msg"` / `--print` — non-interactive passthrough mode.
|
|
3
|
+
*
|
|
4
|
+
* Exercises the early-exit preconditions (must be logged in + must have a
|
|
5
|
+
* project in cwd) since the success path shells out to `claude` which isn't
|
|
6
|
+
* available in CI. Also verifies banner output is suppressed from stdout so
|
|
7
|
+
* the child's stream-json stays clean when a relay pipes the output.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it } from 'node:test';
|
|
10
|
+
import assert from 'node:assert/strict';
|
|
11
|
+
import { mkdirSync, mkdtempSync, writeFileSync } from 'fs';
|
|
12
|
+
import { join } from 'path';
|
|
13
|
+
import { tmpdir } from 'os';
|
|
14
|
+
import { runCli } from './helpers/spawn-cli.js';
|
|
15
|
+
function freshHome() {
|
|
16
|
+
return mkdtempSync(`${tmpdir()}/gipity-cli-claude-test-`);
|
|
17
|
+
}
|
|
18
|
+
function writeFakeAuth(home) {
|
|
19
|
+
const dir = join(home, '.gipity');
|
|
20
|
+
mkdirSync(dir, { recursive: true });
|
|
21
|
+
// Far-future expiry so refreshTokenIfNeeded() is a no-op.
|
|
22
|
+
const expiresAt = new Date(Date.now() + 3600 * 1000).toISOString();
|
|
23
|
+
writeFileSync(join(dir, 'auth.json'), JSON.stringify({
|
|
24
|
+
accessToken: 'fake.jwt.token',
|
|
25
|
+
refreshToken: 'fake-refresh',
|
|
26
|
+
email: 'ec-test@914-6.com',
|
|
27
|
+
expiresAt,
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
describe('gipity claude -p (non-interactive)', () => {
|
|
31
|
+
it('errors out when not logged in, writing the error to stderr and exiting non-zero', () => {
|
|
32
|
+
const home = freshHome();
|
|
33
|
+
const r = runCli(['claude', '-p', 'hello world'], { env: { HOME: home }, cwd: home });
|
|
34
|
+
assert.notEqual(r.status, 0, 'should exit non-zero');
|
|
35
|
+
assert.match(r.stderr, /Not logged in/);
|
|
36
|
+
// Stdout must stay clean so a relay piping `claude -p`'s stream-json
|
|
37
|
+
// isn't polluted with gipity banner text.
|
|
38
|
+
assert.equal(r.stdout, '', `expected empty stdout, got: ${r.stdout}`);
|
|
39
|
+
});
|
|
40
|
+
it('errors out when cwd has no Gipity project, even if logged in', () => {
|
|
41
|
+
const home = freshHome();
|
|
42
|
+
writeFakeAuth(home);
|
|
43
|
+
const r = runCli(['claude', '-p', 'hello'], { env: { HOME: home }, cwd: home });
|
|
44
|
+
assert.notEqual(r.status, 0);
|
|
45
|
+
assert.match(r.stderr, /No Gipity project in cwd/);
|
|
46
|
+
assert.equal(r.stdout, '');
|
|
47
|
+
});
|
|
48
|
+
it('treats --print the same as -p', () => {
|
|
49
|
+
const home = freshHome();
|
|
50
|
+
const r = runCli(['claude', '--print', 'hello'], { env: { HOME: home }, cwd: home });
|
|
51
|
+
assert.notEqual(r.status, 0);
|
|
52
|
+
assert.match(r.stderr, /Not logged in/);
|
|
53
|
+
assert.equal(r.stdout, '');
|
|
54
|
+
});
|
|
55
|
+
it('interactive mode (no -p) still prints the banner to stdout', () => {
|
|
56
|
+
// No auth → auth prompt starts, which reads stdin and we close stdin
|
|
57
|
+
// so it exits. We only need to verify the banner surfaced on stdout
|
|
58
|
+
// (proving the non-interactive stderr-routing is scoped correctly).
|
|
59
|
+
const home = freshHome();
|
|
60
|
+
const r = runCli(['claude'], { env: { HOME: home }, cwd: home, timeout: 3000 });
|
|
61
|
+
// No assertion on status (may time out awaiting input); assert banner presence.
|
|
62
|
+
assert.match(r.stdout, /Gipity CLI/);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=claude-noninteractive.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-noninteractive.test.js","sourceRoot":"","sources":["../../src/__tests__/claude-noninteractive.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Real platform e2e tests. Skipped unless GIPITY_E2E=1 is set.
|
|
2
|
+
//
|
|
3
|
+
// Cost profile: ~$0.001 (one short LLM turn for `chat`); everything else is
|
|
4
|
+
// free platform CRUD. Uses dev-bypass auth (magic code 914914) with an
|
|
5
|
+
// `ec-` prefixed @914-6.com email so the platform suppresses real outbound
|
|
6
|
+
// mail (see platform/CLAUDE.md).
|
|
7
|
+
//
|
|
8
|
+
// Defaults can be overridden by env:
|
|
9
|
+
// GIPITY_E2E=1 enable the suite
|
|
10
|
+
// GIPITY_E2E_API_BASE=... default https://a.gipity.ai
|
|
11
|
+
// GIPITY_E2E_EMAIL=ec-cli-e2e@914-6.com
|
|
12
|
+
// GIPITY_E2E_CODE=914914
|
|
13
|
+
import { describe, it, before, after } from 'node:test';
|
|
14
|
+
import assert from 'node:assert/strict';
|
|
15
|
+
import { mkdtempSync, rmSync, existsSync } from 'fs';
|
|
16
|
+
import { tmpdir } from 'os';
|
|
17
|
+
import { join } from 'path';
|
|
18
|
+
import { runCli, makeTmpHome } from './helpers/spawn-cli.js';
|
|
19
|
+
const E2E_ENABLED = process.env['GIPITY_E2E'] === '1';
|
|
20
|
+
const API_BASE = process.env['GIPITY_E2E_API_BASE'] ?? 'https://a.gipity.ai';
|
|
21
|
+
const EMAIL = process.env['GIPITY_E2E_EMAIL'] ?? 'ec-cli-e2e@914-6.com';
|
|
22
|
+
const CODE = process.env['GIPITY_E2E_CODE'] ?? '914914';
|
|
23
|
+
// Email convention guard — protect against accidentally invoking real SendGrid.
|
|
24
|
+
if (E2E_ENABLED && !EMAIL.startsWith('ec')) {
|
|
25
|
+
throw new Error(`E2E test email must start with "ec" to suppress real outbound mail: got "${EMAIL}"`);
|
|
26
|
+
}
|
|
27
|
+
describe('cli-e2e-live', { skip: !E2E_ENABLED && 'set GIPITY_E2E=1 to run' }, () => {
|
|
28
|
+
const tmpHome = makeTmpHome();
|
|
29
|
+
const projectDir = mkdtempSync(join(tmpdir(), 'gipity-e2e-proj-'));
|
|
30
|
+
const projectSlug = `gip-e2e-${Date.now().toString(36)}`;
|
|
31
|
+
const env = { HOME: tmpHome };
|
|
32
|
+
// All commands run inside projectDir against the test API base, with our
|
|
33
|
+
// throwaway HOME so we never touch the developer's real ~/.gipity/auth.json.
|
|
34
|
+
const cli = (args, opts = {}) => runCli(['--api-base', API_BASE, ...args], {
|
|
35
|
+
env,
|
|
36
|
+
cwd: opts.cwd ?? projectDir,
|
|
37
|
+
timeout: opts.timeout ?? 60000,
|
|
38
|
+
enableUpdater: false,
|
|
39
|
+
});
|
|
40
|
+
before(() => {
|
|
41
|
+
const r = cli(['login', '--email', EMAIL, '--code', CODE]);
|
|
42
|
+
assert.equal(r.status, 0, `login failed: ${r.stderr || r.stdout}`);
|
|
43
|
+
});
|
|
44
|
+
after(() => {
|
|
45
|
+
// Best-effort cleanup; don't fail the whole suite if these go wrong.
|
|
46
|
+
try {
|
|
47
|
+
cli(['-y', 'project', 'delete', projectSlug]);
|
|
48
|
+
}
|
|
49
|
+
catch { /* ignore */ }
|
|
50
|
+
try {
|
|
51
|
+
cli(['logout']);
|
|
52
|
+
}
|
|
53
|
+
catch { /* ignore */ }
|
|
54
|
+
try {
|
|
55
|
+
rmSync(tmpHome, { recursive: true, force: true });
|
|
56
|
+
}
|
|
57
|
+
catch { /* ignore */ }
|
|
58
|
+
try {
|
|
59
|
+
rmSync(projectDir, { recursive: true, force: true });
|
|
60
|
+
}
|
|
61
|
+
catch { /* ignore */ }
|
|
62
|
+
});
|
|
63
|
+
it('1. auth round-trip', () => {
|
|
64
|
+
const r = cli(['status', '--json']);
|
|
65
|
+
assert.equal(r.status, 0);
|
|
66
|
+
const data = JSON.parse(r.stdout);
|
|
67
|
+
assert.equal(data.auth?.email, EMAIL);
|
|
68
|
+
assert.equal(data.auth?.valid, true);
|
|
69
|
+
});
|
|
70
|
+
it('2. project lifecycle: init → status shows project', () => {
|
|
71
|
+
const r = cli(['init', projectSlug]);
|
|
72
|
+
assert.equal(r.status, 0, `init failed: ${r.stderr || r.stdout}`);
|
|
73
|
+
const s = cli(['status', '--json']);
|
|
74
|
+
const data = JSON.parse(s.stdout);
|
|
75
|
+
assert.equal(data.project?.slug, projectSlug);
|
|
76
|
+
});
|
|
77
|
+
it('3. scaffold --type api creates expected files', () => {
|
|
78
|
+
const r = cli(['scaffold', '--type', 'api']);
|
|
79
|
+
assert.equal(r.status, 0, `scaffold failed: ${r.stderr || r.stdout}`);
|
|
80
|
+
assert.ok(existsSync(join(projectDir, 'gipity.yaml')), 'gipity.yaml missing');
|
|
81
|
+
assert.ok(existsSync(join(projectDir, 'functions')), 'functions/ missing');
|
|
82
|
+
assert.ok(existsSync(join(projectDir, 'tests')), 'tests/ missing');
|
|
83
|
+
});
|
|
84
|
+
it('4a. deploy dev succeeds (first deploy)', () => {
|
|
85
|
+
const r = cli(['deploy', 'dev'], { timeout: 120000 });
|
|
86
|
+
assert.equal(r.status, 0, `deploy failed: ${r.stderr || r.stdout}`);
|
|
87
|
+
});
|
|
88
|
+
it('4b. deploy dev again is idempotent (no changes)', () => {
|
|
89
|
+
const r = cli(['deploy', 'dev'], { timeout: 60000 });
|
|
90
|
+
assert.equal(r.status, 0);
|
|
91
|
+
// No strict assertion on output text — phases may say "skipped" or "ok"
|
|
92
|
+
// depending on whether checksums caught everything. Just confirm exit 0.
|
|
93
|
+
});
|
|
94
|
+
it('4c. deploy dev --only functions filters phases', () => {
|
|
95
|
+
const r = cli(['deploy', 'dev', '--only', 'functions'], { timeout: 60000 });
|
|
96
|
+
assert.equal(r.status, 0);
|
|
97
|
+
});
|
|
98
|
+
it('4d. deploy dev --force re-runs all phases', () => {
|
|
99
|
+
const r = cli(['deploy', 'dev', '--force'], { timeout: 120000 });
|
|
100
|
+
assert.equal(r.status, 0);
|
|
101
|
+
});
|
|
102
|
+
it('5a. fn list shows the scaffolded get-weather function', () => {
|
|
103
|
+
const r = cli(['fn', 'list', '--json']);
|
|
104
|
+
assert.equal(r.status, 0);
|
|
105
|
+
const fns = JSON.parse(r.stdout);
|
|
106
|
+
assert.ok(Array.isArray(fns));
|
|
107
|
+
assert.ok(fns.some((f) => f.name === 'get-weather'), 'get-weather not in fn list');
|
|
108
|
+
});
|
|
109
|
+
it('5b. fn call get-weather returns weather data', () => {
|
|
110
|
+
const r = cli(['fn', 'call', 'get-weather', '{"zip":"94103"}'], { timeout: 30000 });
|
|
111
|
+
assert.equal(r.status, 0, `fn call failed: ${r.stderr || r.stdout}`);
|
|
112
|
+
assert.match(r.stdout, /temperature|weather|°|condition/i);
|
|
113
|
+
});
|
|
114
|
+
it('6a. db list succeeds', () => {
|
|
115
|
+
const r = cli(['db', 'list', '--json']);
|
|
116
|
+
assert.equal(r.status, 0, `db list failed: ${r.stderr || r.stdout}`);
|
|
117
|
+
});
|
|
118
|
+
it('6b. db query "select 1" returns 1', () => {
|
|
119
|
+
const r = cli(['db', 'query', 'select 1 as n', '--json']);
|
|
120
|
+
assert.equal(r.status, 0, `db query failed: ${r.stderr || r.stdout}`);
|
|
121
|
+
assert.match(r.stdout, /"n"\s*:\s*1/);
|
|
122
|
+
});
|
|
123
|
+
it('7. chat "what is 2+2?" returns a 4', () => {
|
|
124
|
+
const r = cli(['chat', 'what is 2+2? respond with only the number.'], { timeout: 60000 });
|
|
125
|
+
assert.equal(r.status, 0, `chat failed: ${r.stderr || r.stdout}`);
|
|
126
|
+
assert.match(r.stdout, /\b4\b/);
|
|
127
|
+
});
|
|
128
|
+
it('8. memory write/read/delete round-trip', () => {
|
|
129
|
+
const w = cli(['memory', 'write', 'e2e-topic', 'ping']);
|
|
130
|
+
assert.equal(w.status, 0, `memory write failed: ${w.stderr || w.stdout}`);
|
|
131
|
+
const r = cli(['memory', 'read', 'e2e-topic']);
|
|
132
|
+
assert.equal(r.status, 0);
|
|
133
|
+
assert.match(r.stdout, /ping/);
|
|
134
|
+
const d = cli(['-y', 'memory', 'delete', 'e2e-topic']);
|
|
135
|
+
assert.equal(d.status, 0, `memory delete failed: ${d.stderr || d.stdout}`);
|
|
136
|
+
});
|
|
137
|
+
it('9. doctor reports sane install info with auth', () => {
|
|
138
|
+
const r = cli(['doctor']);
|
|
139
|
+
assert.equal(r.status, 0);
|
|
140
|
+
assert.match(r.stdout, /Gipity CLI — doctor/);
|
|
141
|
+
assert.match(r.stdout, /shim version/);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
//# sourceMappingURL=cli-e2e-live.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-e2e-live.test.js","sourceRoot":"","sources":["../../src/__tests__/cli-e2e-live.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { dirname, resolve } from 'path';
|
|
6
|
+
import { runCli } from './helpers/spawn-cli.js';
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const PKG_VERSION = JSON.parse(readFileSync(resolve(__dirname, '..', '..', 'package.json'), 'utf-8')).version;
|
|
9
|
+
describe('cli-smoke: --version and --help', () => {
|
|
10
|
+
it('--version prints the package version', () => {
|
|
11
|
+
const r = runCli(['--version']);
|
|
12
|
+
assert.equal(r.status, 0);
|
|
13
|
+
assert.equal(r.stdout.trim(), PKG_VERSION);
|
|
14
|
+
});
|
|
15
|
+
it('--help shows version banner near the top and grouped sections in order', () => {
|
|
16
|
+
const r = runCli(['--help']);
|
|
17
|
+
assert.equal(r.status, 0);
|
|
18
|
+
const out = r.stdout;
|
|
19
|
+
assert.match(out, new RegExp(`Gipity CLI\\s+v${PKG_VERSION.replace(/\./g, '\\.')}`));
|
|
20
|
+
const sections = ['Setup:', 'Project:', 'Resources:', 'Agent:', 'Maintenance:'];
|
|
21
|
+
let lastIdx = -1;
|
|
22
|
+
for (const s of sections) {
|
|
23
|
+
const idx = out.indexOf(s);
|
|
24
|
+
assert.ok(idx > -1, `missing section header: ${s}`);
|
|
25
|
+
assert.ok(idx > lastIdx, `section out of order: ${s} appeared before previous one`);
|
|
26
|
+
lastIdx = idx;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
it('--help lists doctor and update under Maintenance', () => {
|
|
30
|
+
const r = runCli(['--help']);
|
|
31
|
+
const maint = r.stdout.split('Maintenance:')[1] ?? '';
|
|
32
|
+
assert.match(maint, /\bdoctor\b/);
|
|
33
|
+
assert.match(maint, /\bupdate\b/);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe('cli-smoke: doctor', () => {
|
|
37
|
+
it('runs cleanly and reports auto-updates disabled when env is set', () => {
|
|
38
|
+
const r = runCli(['doctor']);
|
|
39
|
+
assert.equal(r.status, 0);
|
|
40
|
+
assert.match(r.stdout, /auto-updates/);
|
|
41
|
+
assert.match(r.stdout, /disabled \(DISABLE_AUTOUPDATER=1\)/);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe('cli-smoke: error behavior', () => {
|
|
45
|
+
it('unknown command exits non-zero', () => {
|
|
46
|
+
const r = runCli(['definitely-not-a-real-command']);
|
|
47
|
+
assert.notEqual(r.status, 0);
|
|
48
|
+
});
|
|
49
|
+
it('status without auth prints not-logged-in message', () => {
|
|
50
|
+
const r = runCli(['status']);
|
|
51
|
+
// status returns 0 even when not logged in (it's a status report)
|
|
52
|
+
const combined = r.stdout + r.stderr;
|
|
53
|
+
assert.match(combined, /not logged in|Not a Gipity project/i);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe('cli-smoke: subcommand --help wiring', () => {
|
|
57
|
+
for (const cmd of ['chat', 'deploy', 'db', 'fn', 'memory', 'scaffold', 'login', 'doctor', 'update']) {
|
|
58
|
+
it(`gipity ${cmd} --help exits 0`, () => {
|
|
59
|
+
const r = runCli([cmd, '--help']);
|
|
60
|
+
assert.equal(r.status, 0, `${cmd} --help failed: ${r.stderr}`);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
//# sourceMappingURL=cli-smoke.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-smoke.test.js","sourceRoot":"","sources":["../../src/__tests__/cli-smoke.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { normalizeAliases } from '../flag-aliases.js';
|
|
4
|
+
describe('normalizeAliases', () => {
|
|
5
|
+
it('rewrites --out to --output', () => {
|
|
6
|
+
assert.deepEqual(normalizeAliases(['node', 'gipity', 'generate', 'image', 'prompt', '--out', 'rice.jpg']), ['node', 'gipity', 'generate', 'image', 'prompt', '--output', 'rice.jpg']);
|
|
7
|
+
});
|
|
8
|
+
it('leaves canonical --output unchanged', () => {
|
|
9
|
+
assert.deepEqual(normalizeAliases(['--output', 'rice.jpg']), ['--output', 'rice.jpg']);
|
|
10
|
+
});
|
|
11
|
+
it('rewrites --out=value equals form', () => {
|
|
12
|
+
assert.deepEqual(normalizeAliases(['--out=rice.jpg']), ['--output=rice.jpg']);
|
|
13
|
+
});
|
|
14
|
+
it('rewrites --db to --database', () => {
|
|
15
|
+
assert.deepEqual(normalizeAliases(['db', 'query', 'select 1', '--db', 'mydb']), ['db', 'query', 'select 1', '--database', 'mydb']);
|
|
16
|
+
});
|
|
17
|
+
it('rewrites multiple aliases in one argv', () => {
|
|
18
|
+
assert.deepEqual(normalizeAliases(['--proj', 'foo', '--aspect', '16:9', '--out', 'x.png']), ['--project', 'foo', '--aspect-ratio', '16:9', '--output', 'x.png']);
|
|
19
|
+
});
|
|
20
|
+
it('leaves unrelated long flags alone', () => {
|
|
21
|
+
assert.deepEqual(normalizeAliases(['--quality', 'high', '--unknown-flag', 'v']), ['--quality', 'high', '--unknown-flag', 'v']);
|
|
22
|
+
});
|
|
23
|
+
it('does not rewrite short flags', () => {
|
|
24
|
+
assert.deepEqual(normalizeAliases(['-o', 'x.png']), ['-o', 'x.png']);
|
|
25
|
+
});
|
|
26
|
+
it('does not rewrite positional values', () => {
|
|
27
|
+
assert.deepEqual(normalizeAliases(['fn', 'call', 'foo', '{"x":"--out"}']), ['fn', 'call', 'foo', '{"x":"--out"}']);
|
|
28
|
+
});
|
|
29
|
+
it('does not rewrite a flag whose prefix matches an alias', () => {
|
|
30
|
+
assert.deepEqual(normalizeAliases(['--output-dir']), ['--output-dir']);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=flag-aliases.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flag-aliases.test.js","sourceRoot":"","sources":["../../src/__tests__/flag-aliases.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,SAAS,CACd,gBAAgB,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,EACxF,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAC1E,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,SAAS,CACd,gBAAgB,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,EAC1C,CAAC,UAAU,EAAE,UAAU,CAAC,CACzB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,SAAS,CACd,gBAAgB,CAAC,CAAC,gBAAgB,CAAC,CAAC,EACpC,CAAC,mBAAmB,CAAC,CACtB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,SAAS,CACd,gBAAgB,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAC7D,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,CAAC,CAClD,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CAAC,SAAS,CACd,gBAAgB,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,EACzE,CAAC,WAAW,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,CACpE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,SAAS,CACd,gBAAgB,CAAC,CAAC,WAAW,EAAE,MAAM,EAAE,gBAAgB,EAAE,GAAG,CAAC,CAAC,EAC9D,CAAC,WAAW,EAAE,MAAM,EAAE,gBAAgB,EAAE,GAAG,CAAC,CAC7C,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,SAAS,CACd,gBAAgB,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,EACxD,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,CAAC,CACvC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,CAAC,SAAS,CACd,gBAAgB,CAAC,CAAC,cAAc,CAAC,CAAC,EAClC,CAAC,cAAc,CAAC,CACjB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare const CLI_ENTRY: string;
|
|
2
|
+
export interface SpawnResult {
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
status: number;
|
|
6
|
+
}
|
|
7
|
+
export interface SpawnOptions {
|
|
8
|
+
/** Extra/overriding env vars (merged on top of the sanitized base env). */
|
|
9
|
+
env?: Record<string, string>;
|
|
10
|
+
/** Working directory for the child process. */
|
|
11
|
+
cwd?: string;
|
|
12
|
+
/** ms before the spawn is killed. Default 15000. */
|
|
13
|
+
timeout?: number;
|
|
14
|
+
/** If true, do NOT set DISABLE_AUTOUPDATER (Tier C wants normal startup). */
|
|
15
|
+
enableUpdater?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Run the built gipity CLI with a deterministic, isolated environment.
|
|
19
|
+
* Returns combined stdout/stderr text plus exit status.
|
|
20
|
+
*/
|
|
21
|
+
export declare function runCli(args: string[], opts?: SpawnOptions): SpawnResult;
|
|
22
|
+
export declare function makeTmpHome(): string;
|
|
23
|
+
/**
|
|
24
|
+
* Async version of runCli — uses `spawn` instead of `spawnSync` so the
|
|
25
|
+
* test's event loop keeps turning while the child runs. Required for any
|
|
26
|
+
* test that spins up an in-process HTTP server for the child to hit
|
|
27
|
+
* (spawnSync deadlocks because the server can't accept connections while
|
|
28
|
+
* the event loop is blocked).
|
|
29
|
+
*/
|
|
30
|
+
export declare function runCliAsync(args: string[], opts?: SpawnOptions): Promise<SpawnResult>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { spawnSync, spawn } from 'child_process';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { dirname, resolve } from 'path';
|
|
4
|
+
import { mkdtempSync } from 'fs';
|
|
5
|
+
import { tmpdir } from 'os';
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
// __dirname is dist/__tests__/helpers — CLI entry is dist/index.js
|
|
8
|
+
export const CLI_ENTRY = resolve(__dirname, '..', '..', 'index.js');
|
|
9
|
+
/**
|
|
10
|
+
* Run the built gipity CLI with a deterministic, isolated environment.
|
|
11
|
+
* Returns combined stdout/stderr text plus exit status.
|
|
12
|
+
*/
|
|
13
|
+
export function runCli(args, opts = {}) {
|
|
14
|
+
const baseEnv = {
|
|
15
|
+
PATH: process.env['PATH'] ?? '',
|
|
16
|
+
HOME: opts.env?.['HOME'] ?? mkdtempSync(`${tmpdir()}/gipity-cli-test-`),
|
|
17
|
+
NO_COLOR: '1',
|
|
18
|
+
CI: '1',
|
|
19
|
+
};
|
|
20
|
+
if (!opts.enableUpdater)
|
|
21
|
+
baseEnv['DISABLE_AUTOUPDATER'] = '1';
|
|
22
|
+
const env = { ...baseEnv, ...opts.env };
|
|
23
|
+
const spawnOpts = {
|
|
24
|
+
encoding: 'utf-8',
|
|
25
|
+
cwd: opts.cwd ?? process.cwd(),
|
|
26
|
+
env,
|
|
27
|
+
timeout: opts.timeout ?? 15000,
|
|
28
|
+
};
|
|
29
|
+
const res = spawnSync(process.execPath, [CLI_ENTRY, ...args], spawnOpts);
|
|
30
|
+
return {
|
|
31
|
+
stdout: res.stdout ?? '',
|
|
32
|
+
stderr: res.stderr ?? '',
|
|
33
|
+
status: res.status ?? -1,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function makeTmpHome() {
|
|
37
|
+
return mkdtempSync(`${tmpdir()}/gipity-cli-test-`);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Async version of runCli — uses `spawn` instead of `spawnSync` so the
|
|
41
|
+
* test's event loop keeps turning while the child runs. Required for any
|
|
42
|
+
* test that spins up an in-process HTTP server for the child to hit
|
|
43
|
+
* (spawnSync deadlocks because the server can't accept connections while
|
|
44
|
+
* the event loop is blocked).
|
|
45
|
+
*/
|
|
46
|
+
export async function runCliAsync(args, opts = {}) {
|
|
47
|
+
const baseEnv = {
|
|
48
|
+
PATH: process.env['PATH'] ?? '',
|
|
49
|
+
HOME: opts.env?.['HOME'] ?? mkdtempSync(`${tmpdir()}/gipity-cli-test-`),
|
|
50
|
+
NO_COLOR: '1',
|
|
51
|
+
CI: '1',
|
|
52
|
+
};
|
|
53
|
+
if (!opts.enableUpdater)
|
|
54
|
+
baseEnv['DISABLE_AUTOUPDATER'] = '1';
|
|
55
|
+
const env = { ...baseEnv, ...opts.env };
|
|
56
|
+
const child = spawn(process.execPath, [CLI_ENTRY, ...args], {
|
|
57
|
+
cwd: opts.cwd ?? process.cwd(),
|
|
58
|
+
env,
|
|
59
|
+
});
|
|
60
|
+
let stdout = '';
|
|
61
|
+
let stderr = '';
|
|
62
|
+
child.stdout.on('data', c => { stdout += c; });
|
|
63
|
+
child.stderr.on('data', c => { stderr += c; });
|
|
64
|
+
const timeoutMs = opts.timeout ?? 15000;
|
|
65
|
+
const timer = setTimeout(() => child.kill('SIGKILL'), timeoutMs);
|
|
66
|
+
const status = await new Promise(resolve => {
|
|
67
|
+
child.on('exit', code => { clearTimeout(timer); resolve(code ?? -1); });
|
|
68
|
+
child.on('error', () => { clearTimeout(timer); resolve(-1); });
|
|
69
|
+
});
|
|
70
|
+
return { stdout, stderr, status };
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=spawn-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn-cli.js","sourceRoot":"","sources":["../../../src/__tests__/helpers/spawn-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAsC,MAAM,eAAe,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAE5B,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,mEAAmE;AACnE,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;AAmBpE;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,IAAc,EAAE,OAAqB,EAAE;IAC5D,MAAM,OAAO,GAA2B;QACtC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE;QAC/B,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,WAAW,CAAC,GAAG,MAAM,EAAE,mBAAmB,CAAC;QACvE,QAAQ,EAAE,GAAG;QACb,EAAE,EAAE,GAAG;KACR,CAAC;IACF,IAAI,CAAC,IAAI,CAAC,aAAa;QAAE,OAAO,CAAC,qBAAqB,CAAC,GAAG,GAAG,CAAC;IAE9D,MAAM,GAAG,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAExC,MAAM,SAAS,GAAuC;QACpD,QAAQ,EAAE,OAAO;QACjB,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;QAC9B,GAAG;QACH,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,KAAK;KAC/B,CAAC;IAEF,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC;IACzE,OAAO;QACL,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE;QACxB,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE;QACxB,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC;KACzB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,WAAW,CAAC,GAAG,MAAM,EAAE,mBAAmB,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAc,EAAE,OAAqB,EAAE;IACvE,MAAM,OAAO,GAA2B;QACtC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE;QAC/B,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,WAAW,CAAC,GAAG,MAAM,EAAE,mBAAmB,CAAC;QACvE,QAAQ,EAAE,GAAG;QACb,EAAE,EAAE,GAAG;KACR,CAAC;IACF,IAAI,CAAC,IAAI,CAAC,aAAa;QAAE,OAAO,CAAC,qBAAqB,CAAC,GAAG,GAAG,CAAC;IAC9D,MAAM,GAAG,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAExC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,EAAE;QAC1D,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;QAC9B,GAAG;KACJ,CAAC,CAAC;IAEH,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE/C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC;IACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC;IAEjE,MAAM,MAAM,GAAW,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;QACjD,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `gipity hook-capture <event>` — hidden subcommand wired into Claude Code
|
|
3
|
+
* hooks. Runs in every Claude Code turn (even in directories that aren't
|
|
4
|
+
* Gipity projects) and MUST never disrupt the session: silent no-op when
|
|
5
|
+
* there's no .gipity.json or no auth, exit 0 on bad input.
|
|
6
|
+
*
|
|
7
|
+
* We can't exercise the happy path here (needs a live backend). These
|
|
8
|
+
* smoke tests cover the "must never break Claude Code" contract.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it } from 'node:test';
|
|
11
|
+
import assert from 'node:assert/strict';
|
|
12
|
+
import { spawnSync } from 'child_process';
|
|
13
|
+
import { mkdtempSync } from 'fs';
|
|
14
|
+
import { tmpdir } from 'os';
|
|
15
|
+
import { CLI_ENTRY } from './helpers/spawn-cli.js';
|
|
16
|
+
function runWithStdin(args, stdin, cwd) {
|
|
17
|
+
const home = mkdtempSync(`${tmpdir()}/gipity-hc-test-`);
|
|
18
|
+
const res = spawnSync(process.execPath, [CLI_ENTRY, ...args], {
|
|
19
|
+
encoding: 'utf-8',
|
|
20
|
+
cwd: cwd ?? home,
|
|
21
|
+
env: {
|
|
22
|
+
PATH: process.env['PATH'] ?? '',
|
|
23
|
+
HOME: home,
|
|
24
|
+
NO_COLOR: '1',
|
|
25
|
+
CI: '1',
|
|
26
|
+
DISABLE_AUTOUPDATER: '1',
|
|
27
|
+
},
|
|
28
|
+
input: stdin,
|
|
29
|
+
timeout: 10_000,
|
|
30
|
+
});
|
|
31
|
+
return { stdout: res.stdout ?? '', stderr: res.stderr ?? '', status: res.status ?? -1 };
|
|
32
|
+
}
|
|
33
|
+
describe('gipity hook-capture: silent-no-op contract', () => {
|
|
34
|
+
const validPayload = JSON.stringify({
|
|
35
|
+
session_id: 'sid_test_12345',
|
|
36
|
+
tool_name: 'Bash',
|
|
37
|
+
tool_input: { command: 'ls' },
|
|
38
|
+
tool_response: { stdout: 'a\nb', exit_code: 0 },
|
|
39
|
+
});
|
|
40
|
+
it('exits 0 silently when no .gipity.json and no auth (hottest path)', () => {
|
|
41
|
+
const r = runWithStdin(['hook-capture', 'tool'], validPayload);
|
|
42
|
+
assert.equal(r.status, 0, `expected exit 0, got ${r.status}. stderr: ${r.stderr}`);
|
|
43
|
+
assert.equal(r.stdout, '', `expected no stdout, got: ${r.stdout}`);
|
|
44
|
+
assert.equal(r.stderr, '', `expected no stderr, got: ${r.stderr}`);
|
|
45
|
+
});
|
|
46
|
+
it('exits 0 silently on malformed JSON stdin', () => {
|
|
47
|
+
const r = runWithStdin(['hook-capture', 'prompt'], 'not json {{{{');
|
|
48
|
+
assert.equal(r.status, 0);
|
|
49
|
+
assert.equal(r.stdout, '');
|
|
50
|
+
assert.equal(r.stderr, '');
|
|
51
|
+
});
|
|
52
|
+
it('exits 0 silently on an unknown event name', () => {
|
|
53
|
+
const r = runWithStdin(['hook-capture', 'totally-made-up'], validPayload);
|
|
54
|
+
assert.equal(r.status, 0);
|
|
55
|
+
assert.equal(r.stdout, '');
|
|
56
|
+
assert.equal(r.stderr, '');
|
|
57
|
+
});
|
|
58
|
+
it('exits 0 silently on empty stdin', () => {
|
|
59
|
+
const r = runWithStdin(['hook-capture', 'stop'], '');
|
|
60
|
+
assert.equal(r.status, 0);
|
|
61
|
+
assert.equal(r.stdout, '');
|
|
62
|
+
assert.equal(r.stderr, '');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=hook-capture.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-capture.test.js","sourceRoot":"","sources":["../../src/__tests__/hook-capture.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|