gipity 1.0.272 → 1.0.318
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 +16 -13
- package/dist/__tests__/capture-transcript.test.d.ts +1 -0
- package/dist/__tests__/capture-transcript.test.js +92 -0
- package/dist/__tests__/capture-transcript.test.js.map +1 -0
- package/dist/__tests__/claude-noninteractive.test.js +1 -1
- package/dist/__tests__/cli-e2e-live.test.js +3 -3
- package/dist/__tests__/cli-smoke.test.js +5 -5
- package/dist/__tests__/cli-smoke.test.js.map +1 -1
- package/dist/__tests__/helpers/spawn-cli.d.ts +1 -1
- package/dist/__tests__/helpers/spawn-cli.js +2 -2
- package/dist/__tests__/prompts.test.d.ts +1 -0
- package/dist/__tests__/prompts.test.js +129 -0
- package/dist/__tests__/prompts.test.js.map +1 -0
- package/dist/__tests__/push-cas.test.d.ts +1 -0
- package/dist/__tests__/push-cas.test.js +133 -0
- package/dist/__tests__/push-cas.test.js.map +1 -0
- package/dist/__tests__/relay-bridge-abort.test.js +2 -2
- package/dist/__tests__/relay-daemon.test.js +5 -5
- package/dist/__tests__/relay-ingest-contract.test.js +2 -2
- package/dist/__tests__/relay-installers.test.js +4 -4
- package/dist/__tests__/relay-state.test.js +1 -1
- package/dist/__tests__/stream-json.test.js +2 -2
- package/dist/__tests__/sync-apply.test.d.ts +1 -0
- package/dist/__tests__/sync-apply.test.js +457 -0
- package/dist/__tests__/sync-apply.test.js.map +1 -0
- package/dist/__tests__/sync-lock.test.d.ts +1 -0
- package/dist/__tests__/sync-lock.test.js +105 -0
- package/dist/__tests__/sync-lock.test.js.map +1 -0
- package/dist/__tests__/sync.test.js +115 -151
- package/dist/__tests__/sync.test.js.map +1 -1
- package/dist/__tests__/updater.test.js +1 -1
- package/dist/adopt-cwd.d.ts +66 -0
- package/dist/adopt-cwd.js +255 -0
- package/dist/adopt-cwd.js.map +1 -0
- package/dist/api.d.ts +12 -2
- package/dist/api.js +47 -4
- package/dist/api.js.map +1 -1
- package/dist/auth.js +1 -1
- package/dist/banner.js +29 -29
- package/dist/banner.js.map +1 -1
- package/dist/capture/sources/claude-code.d.ts +78 -0
- package/dist/capture/sources/claude-code.js +158 -0
- package/dist/capture/sources/claude-code.js.map +1 -0
- package/dist/colors.js +1 -1
- package/dist/commands/agent.js +18 -22
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/api.js +1 -1
- package/dist/commands/approval.d.ts +2 -0
- package/dist/commands/approval.js +117 -0
- package/dist/commands/approval.js.map +1 -0
- package/dist/commands/chat.js +48 -10
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/claude.js +266 -134
- package/dist/commands/claude.js.map +1 -1
- package/dist/commands/credits.js +2 -2
- package/dist/commands/credits.js.map +1 -1
- package/dist/commands/db.js +9 -6
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/deploy.js +2 -2
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/domain.js +2 -2
- package/dist/commands/email.js +30 -5
- package/dist/commands/email.js.map +1 -1
- package/dist/commands/file.js +11 -21
- package/dist/commands/file.js.map +1 -1
- package/dist/commands/fn.js +2 -2
- package/dist/commands/fn.js.map +1 -1
- package/dist/commands/generate.js +2 -2
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/gmail.d.ts +2 -0
- package/dist/commands/gmail.js +85 -0
- package/dist/commands/gmail.js.map +1 -0
- package/dist/commands/init.js +35 -48
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/location.js +1 -1
- package/dist/commands/location.js.map +1 -1
- package/dist/commands/login.js +27 -13
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.js +7 -4
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/logs.js +2 -2
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/memory.js +8 -18
- package/dist/commands/memory.js.map +1 -1
- package/dist/commands/page-inspect.js +37 -26
- package/dist/commands/page-inspect.js.map +1 -1
- package/dist/commands/page-screenshot.d.ts +2 -0
- package/dist/commands/page-screenshot.js +212 -0
- package/dist/commands/page-screenshot.js.map +1 -0
- package/dist/commands/project.js +29 -36
- package/dist/commands/project.js.map +1 -1
- package/dist/commands/push.js +6 -2
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/rbac.js +7 -7
- package/dist/commands/rbac.js.map +1 -1
- package/dist/commands/records.js +10 -8
- package/dist/commands/records.js.map +1 -1
- package/dist/commands/relay-install.d.ts +2 -2
- package/dist/commands/relay-install.js +2 -2
- package/dist/commands/relay-install.js.map +1 -1
- package/dist/commands/relay.d.ts +1 -1
- package/dist/commands/relay.js +15 -15
- package/dist/commands/relay.js.map +1 -1
- package/dist/commands/sandbox.js +3 -3
- package/dist/commands/sandbox.js.map +1 -1
- package/dist/commands/scaffold.js +20 -14
- package/dist/commands/scaffold.js.map +1 -1
- package/dist/commands/skill.d.ts +2 -0
- package/dist/commands/skill.js +45 -0
- package/dist/commands/skill.js.map +1 -0
- package/dist/commands/skills.js +3 -3
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/status.js +2 -2
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/sync.js +15 -24
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/test.js +5 -5
- package/dist/commands/test.js.map +1 -1
- package/dist/commands/uninstall.d.ts +2 -2
- package/dist/commands/uninstall.js +7 -7
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.js +4 -1
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/upload.js +2 -2
- package/dist/commands/upload.js.map +1 -1
- package/dist/commands/workflow.js +51 -22
- package/dist/commands/workflow.js.map +1 -1
- package/dist/config.d.ts +5 -0
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/help-skills.js +1 -1
- package/dist/helpers/command.d.ts +1 -1
- package/dist/helpers/command.js +1 -1
- package/dist/helpers/index.d.ts +2 -2
- package/dist/helpers/index.js +2 -2
- package/dist/helpers/index.js.map +1 -1
- package/dist/helpers/output.d.ts +5 -1
- package/dist/helpers/output.js +15 -2
- package/dist/helpers/output.js.map +1 -1
- package/dist/helpers/sync.d.ts +5 -3
- package/dist/helpers/sync.js +14 -8
- package/dist/helpers/sync.js.map +1 -1
- package/dist/hooks/capture-runner.d.ts +24 -0
- package/dist/hooks/capture-runner.js +233 -0
- package/dist/hooks/capture-runner.js.map +1 -0
- package/dist/index.js +25 -22
- package/dist/index.js.map +1 -1
- package/dist/project-setup.d.ts +3 -4
- package/dist/project-setup.js +9 -11
- package/dist/project-setup.js.map +1 -1
- package/dist/prompts.d.ts +18 -11
- package/dist/prompts.js +68 -45
- package/dist/prompts.js.map +1 -1
- package/dist/provider-docs.d.ts +4 -4
- package/dist/provider-docs.js +6 -6
- package/dist/provider-docs.js.map +1 -1
- package/dist/relay/daemon.d.ts +8 -8
- package/dist/relay/daemon.js +97 -97
- package/dist/relay/daemon.js.map +1 -1
- package/dist/relay/device-http.d.ts +9 -0
- package/dist/relay/device-http.js +58 -0
- package/dist/relay/device-http.js.map +1 -0
- package/dist/relay/installers.d.ts +1 -1
- package/dist/relay/installers.js +11 -11
- package/dist/relay/onboarding.js +6 -6
- package/dist/relay/state.d.ts +1 -1
- package/dist/relay/state.js +6 -6
- package/dist/relay/stream-json.d.ts +3 -3
- package/dist/relay/stream-json.js +4 -4
- package/dist/setup.d.ts +12 -4
- package/dist/setup.js +63 -24
- package/dist/setup.js.map +1 -1
- package/dist/sync.d.ts +55 -37
- package/dist/sync.js +628 -445
- package/dist/sync.js.map +1 -1
- package/dist/updater/bootstrap.d.ts +1 -1
- package/dist/updater/bootstrap.js +2 -2
- package/dist/updater/shim.js +1 -1
- package/dist/upload.d.ts +13 -2
- package/dist/upload.js +62 -9
- package/dist/upload.js.map +1 -1
- package/dist/utils.d.ts +3 -3
- package/dist/utils.js +4 -4
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Gipity CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The agent-tuned platform where AI-built apps live.
|
|
4
4
|
|
|
5
|
-
[Gipity](https://gipity.ai) is
|
|
5
|
+
[Gipity](https://gipity.ai) is the platform: hosting, databases, file storage, deployment, workflows, code execution, and monitoring. Agent-tuned from scaffold to deploy. Use standalone, or pair with Claude Code to give your local agent cloud superpowers. Any model, any infra, always your code.
|
|
6
6
|
|
|
7
|
-
This CLI connects [Claude Code](https://claude.ai/claude-code) to Gipity's cloud platform
|
|
7
|
+
This CLI connects [Claude Code](https://claude.ai/claude-code) to Gipity's cloud platform - databases, deployment, browser testing, image gen, and 50+ other capabilities your local agent doesn't have. It also syncs files so Claude Code and the Gipity web agent share the same project.
|
|
8
8
|
|
|
9
9
|
## Getting Started
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ You need **Node.js 18+** (which includes npm) and **Claude Code**.
|
|
|
16
16
|
# macOS
|
|
17
17
|
brew install node
|
|
18
18
|
|
|
19
|
-
# Windows
|
|
19
|
+
# Windows - download the installer from https://nodejs.org
|
|
20
20
|
|
|
21
21
|
# Linux (Ubuntu/Debian)
|
|
22
22
|
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt install -y nodejs
|
|
@@ -32,7 +32,7 @@ That's it. `claude` walks you through login, project setup, and launches Claude
|
|
|
32
32
|
|
|
33
33
|
## Updates
|
|
34
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/`
|
|
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
36
|
|
|
37
37
|
```bash
|
|
38
38
|
gipity doctor # show install version, last update check, opt-out status
|
|
@@ -75,7 +75,7 @@ That's it. You'll see:
|
|
|
75
75
|
|
|
76
76
|
If you're already logged in, it skips straight to project setup. If you already have a project in the current directory, it skips straight to launching Claude Code.
|
|
77
77
|
|
|
78
|
-
Projects live in `~/GipityProjects/{project-slug}/`
|
|
78
|
+
Projects live in `~/GipityProjects/{project-slug}/` - created automatically on first use. Any extra flags (like `--dangerously-skip-permissions`, `--model opus`, etc.) get passed through to Claude.
|
|
79
79
|
|
|
80
80
|
### The manual way
|
|
81
81
|
|
|
@@ -93,11 +93,11 @@ claude
|
|
|
93
93
|
|
|
94
94
|
This is the good part. When you run `gipity init` in a project, it sets up two hooks in `.claude/settings.json`:
|
|
95
95
|
|
|
96
|
-
**Auto-push**
|
|
96
|
+
**Auto-push** - Every time Claude Code writes or edits a file, it gets pushed to Gipity in the background. No extra steps.
|
|
97
97
|
|
|
98
|
-
**Auto-pull**
|
|
98
|
+
**Auto-pull** - Before each turn, Claude Code pulls any changes that happened remotely (like if your Gipity agent built something via chat). Claude sees what changed and can pick up where things left off.
|
|
99
99
|
|
|
100
|
-
That means Claude Code and your Gipity agent share the same files, same project, same context. You get the best of both
|
|
100
|
+
That means Claude Code and your Gipity agent share the same files, same project, same context. You get the best of both - Claude Code for hands-on coding, Gipity for autonomous agent work.
|
|
101
101
|
|
|
102
102
|
### What gets set up
|
|
103
103
|
|
|
@@ -122,7 +122,7 @@ gipity sync down # Pull remote changes
|
|
|
122
122
|
|
|
123
123
|
| Command | What it does |
|
|
124
124
|
|---------|-------------|
|
|
125
|
-
| `gipity claude` | Log in, pick a project, and launch Claude Code
|
|
125
|
+
| `gipity claude` | Log in, pick a project, and launch Claude Code - all in one |
|
|
126
126
|
| `gipity login` | Authenticate with email + verification code |
|
|
127
127
|
| `gipity init` | Set up a Gipity project and configure Claude Code |
|
|
128
128
|
| `gipity status` | Show project, agent, and auth info |
|
|
@@ -135,6 +135,7 @@ gipity sync down # Pull remote changes
|
|
|
135
135
|
| `gipity sandbox run <code>` | Execute code in a sandboxed container |
|
|
136
136
|
| `gipity project` | List, create, switch, or delete projects |
|
|
137
137
|
| `gipity agent` | List, create, switch, or configure agents |
|
|
138
|
+
| `gipity approval` | List, create, answer, or cancel pending approvals |
|
|
138
139
|
| `gipity workflow` | Manage and trigger automated workflows |
|
|
139
140
|
| `gipity file` | Browse remote files (ls, cat, tree) |
|
|
140
141
|
| `gipity scaffold [title]` | Create app structure (`--type web`, `--type 2d-game`, or `--type 3d-world`) |
|
|
@@ -146,9 +147,11 @@ gipity sync down # Pull remote changes
|
|
|
146
147
|
| `gipity rbac` | Manage RBAC policies |
|
|
147
148
|
| `gipity audit` | Query audit logs |
|
|
148
149
|
| `gipity credits` | Check your balance and usage |
|
|
149
|
-
| `gipity
|
|
150
|
+
| `gipity skill` | List and manage agent skills |
|
|
151
|
+
| `gipity chat [list\|rename\|archive\|delete]` | Manage chats (or `gipity chat <message>` to send) |
|
|
152
|
+
| `gipity gmail [send\|reply\|search\|read]` | Send/read via your own Gmail (different from `gipity email`) |
|
|
150
153
|
| `gipity domain` | Manage custom domains for deployed apps |
|
|
151
|
-
| `gipity email` | Send emails
|
|
154
|
+
| `gipity email [send]` | Send emails from the platform (gipity@gipity.ai) |
|
|
152
155
|
| `gipity generate` | Generate images, audio, or video via your agent |
|
|
153
156
|
| `gipity logout` | Sign out and clear local tokens |
|
|
154
157
|
|
|
@@ -253,7 +256,7 @@ Your login tokens. Created by `gipity login`. Tokens auto-refresh so you shouldn
|
|
|
253
256
|
|
|
254
257
|
## Questions?
|
|
255
258
|
|
|
256
|
-
Reach out anytime
|
|
259
|
+
Reach out anytime - steve@gipity.ai
|
|
257
260
|
|
|
258
261
|
This is early and moving fast. If something's broken or confusing, I want to hear about it.
|
|
259
262
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the Claude Code transcript → ingest mapper used by the
|
|
3
|
+
* capture hook runner. Pure function tests, no file I/O, no network.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it } from 'node:test';
|
|
6
|
+
import assert from 'node:assert/strict';
|
|
7
|
+
import { transcriptLineToEntries, parseTranscript, } from '../capture/sources/claude-code.js';
|
|
8
|
+
describe('transcriptLineToEntries', () => {
|
|
9
|
+
it('skips queue-operation, summary, sidechain, and envelope-only lines', () => {
|
|
10
|
+
assert.deepEqual(transcriptLineToEntries({ type: 'queue-operation', uuid: 'a' }), []);
|
|
11
|
+
assert.deepEqual(transcriptLineToEntries({ type: 'summary', uuid: 'b' }), []);
|
|
12
|
+
assert.deepEqual(transcriptLineToEntries({ type: 'user', uuid: 'c', isSidechain: true, message: { content: 'hi' } }), []);
|
|
13
|
+
assert.deepEqual(transcriptLineToEntries({ type: 'user', uuid: 'd', toolUseResult: { foo: 1 } }), []);
|
|
14
|
+
});
|
|
15
|
+
it('skips lines missing a uuid', () => {
|
|
16
|
+
assert.deepEqual(transcriptLineToEntries({ type: 'user', message: { content: 'hi' } }), []);
|
|
17
|
+
});
|
|
18
|
+
it('maps a string-content user message to a prompt entry tagged with uuid', () => {
|
|
19
|
+
const out = transcriptLineToEntries({ type: 'user', uuid: 'u1', message: { content: 'what is 2+2' } });
|
|
20
|
+
assert.deepEqual(out, [{ kind: 'prompt', prompt: 'what is 2+2', source_uuid: 'u1' }]);
|
|
21
|
+
});
|
|
22
|
+
it('maps a user message with tool_result blocks to one tool_result per block', () => {
|
|
23
|
+
const out = transcriptLineToEntries({
|
|
24
|
+
type: 'user',
|
|
25
|
+
uuid: 'u2',
|
|
26
|
+
message: {
|
|
27
|
+
content: [
|
|
28
|
+
{ type: 'tool_result', tool_use_id: 'tool_a', content: 'ok' },
|
|
29
|
+
{ type: 'tool_result', tool_use_id: 'tool_b', content: 'fail', is_error: true },
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
assert.equal(out.length, 2);
|
|
34
|
+
assert.equal(out[0].kind, 'tool_result');
|
|
35
|
+
assert.equal(out[0].tool_use_id, 'tool_a');
|
|
36
|
+
assert.equal(out[0].source_uuid, 'u2');
|
|
37
|
+
assert.equal(out[1].is_error, true);
|
|
38
|
+
});
|
|
39
|
+
it('maps an assistant message with text + tool_use blocks', () => {
|
|
40
|
+
const out = transcriptLineToEntries({
|
|
41
|
+
type: 'assistant',
|
|
42
|
+
uuid: 'a1',
|
|
43
|
+
message: {
|
|
44
|
+
content: [
|
|
45
|
+
{ type: 'text', text: 'running:' },
|
|
46
|
+
{ type: 'tool_use', id: 'tool_a', name: 'Bash', input: { command: 'ls' } },
|
|
47
|
+
{ type: 'text', text: 'done' },
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
// One assistant entry (with joined text + full blocks) + one tool_use entry
|
|
52
|
+
assert.equal(out.length, 2);
|
|
53
|
+
assert.equal(out[0].kind, 'assistant');
|
|
54
|
+
assert.equal(out[0].text, 'running:\ndone');
|
|
55
|
+
assert.equal(out[0].source_uuid, 'a1');
|
|
56
|
+
assert.equal(out[1].kind, 'tool_use');
|
|
57
|
+
assert.equal(out[1].tool_name, 'Bash');
|
|
58
|
+
assert.equal(out[1].source_uuid, 'a1');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('parseTranscript', () => {
|
|
62
|
+
const user1 = JSON.stringify({ type: 'user', uuid: 'u1', message: { content: 'hello' } });
|
|
63
|
+
const asst1 = JSON.stringify({ type: 'assistant', uuid: 'a1', message: { content: [{ type: 'text', text: 'hi' }] } });
|
|
64
|
+
const user2 = JSON.stringify({ type: 'user', uuid: 'u2', message: { content: 'again' } });
|
|
65
|
+
const asst2 = JSON.stringify({ type: 'assistant', uuid: 'a2', message: { content: [{ type: 'text', text: 'yes' }] } });
|
|
66
|
+
const transcript = [user1, asst1, user2, asst2].join('\n');
|
|
67
|
+
it('emits every entry when watermark is null', () => {
|
|
68
|
+
const r = parseTranscript(transcript, null);
|
|
69
|
+
assert.equal(r.entries.length, 4);
|
|
70
|
+
assert.equal(r.lastUuid, 'a2');
|
|
71
|
+
assert.equal(r.foundWatermark, true);
|
|
72
|
+
});
|
|
73
|
+
it('skips up through the watermark and emits the rest', () => {
|
|
74
|
+
const r = parseTranscript(transcript, 'a1');
|
|
75
|
+
assert.equal(r.entries.length, 2);
|
|
76
|
+
assert.equal(r.entries[0].prompt, 'again');
|
|
77
|
+
assert.equal(r.lastUuid, 'a2');
|
|
78
|
+
assert.equal(r.foundWatermark, true);
|
|
79
|
+
});
|
|
80
|
+
it('reports foundWatermark=false when watermark isn\'t in the transcript', () => {
|
|
81
|
+
const r = parseTranscript(transcript, 'missing-uuid');
|
|
82
|
+
assert.equal(r.foundWatermark, false);
|
|
83
|
+
});
|
|
84
|
+
it('tolerates malformed JSONL lines', () => {
|
|
85
|
+
const junk = transcript + '\n{not json\n' + user2;
|
|
86
|
+
const r = parseTranscript(junk, null);
|
|
87
|
+
// Malformed lines are ignored; duplicate u2 appears twice (dedup is the
|
|
88
|
+
// server's job via source_uuid, not the parser's).
|
|
89
|
+
assert.equal(r.entries.length, 5);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=capture-transcript.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture-transcript.test.js","sourceRoot":"","sources":["../../src/__tests__/capture-transcript.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `gipity claude -p "msg"` / `--print`
|
|
2
|
+
* `gipity claude -p "msg"` / `--print` - non-interactive passthrough mode.
|
|
3
3
|
*
|
|
4
4
|
* Exercises the early-exit preconditions (must be logged in + must have a
|
|
5
5
|
* project in cwd) since the success path shells out to `claude` which isn't
|
|
@@ -20,7 +20,7 @@ const E2E_ENABLED = process.env['GIPITY_E2E'] === '1';
|
|
|
20
20
|
const API_BASE = process.env['GIPITY_E2E_API_BASE'] ?? 'https://a.gipity.ai';
|
|
21
21
|
const EMAIL = process.env['GIPITY_E2E_EMAIL'] ?? 'ec-cli-e2e@914-6.com';
|
|
22
22
|
const CODE = process.env['GIPITY_E2E_CODE'] ?? '914914';
|
|
23
|
-
// Email convention guard
|
|
23
|
+
// Email convention guard - protect against accidentally invoking real SendGrid.
|
|
24
24
|
if (E2E_ENABLED && !EMAIL.startsWith('ec')) {
|
|
25
25
|
throw new Error(`E2E test email must start with "ec" to suppress real outbound mail: got "${EMAIL}"`);
|
|
26
26
|
}
|
|
@@ -88,7 +88,7 @@ describe('cli-e2e-live', { skip: !E2E_ENABLED && 'set GIPITY_E2E=1 to run' }, ()
|
|
|
88
88
|
it('4b. deploy dev again is idempotent (no changes)', () => {
|
|
89
89
|
const r = cli(['deploy', 'dev'], { timeout: 60000 });
|
|
90
90
|
assert.equal(r.status, 0);
|
|
91
|
-
// No strict assertion on output text
|
|
91
|
+
// No strict assertion on output text - phases may say "skipped" or "ok"
|
|
92
92
|
// depending on whether checksums caught everything. Just confirm exit 0.
|
|
93
93
|
});
|
|
94
94
|
it('4c. deploy dev --only functions filters phases', () => {
|
|
@@ -137,7 +137,7 @@ describe('cli-e2e-live', { skip: !E2E_ENABLED && 'set GIPITY_E2E=1 to run' }, ()
|
|
|
137
137
|
it('9. doctor reports sane install info with auth', () => {
|
|
138
138
|
const r = cli(['doctor']);
|
|
139
139
|
assert.equal(r.status, 0);
|
|
140
|
-
assert.match(r.stdout, /Gipity CLI
|
|
140
|
+
assert.match(r.stdout, /Gipity CLI - doctor/);
|
|
141
141
|
assert.match(r.stdout, /shim version/);
|
|
142
142
|
});
|
|
143
143
|
});
|
|
@@ -17,7 +17,7 @@ describe('cli-smoke: --version and --help', () => {
|
|
|
17
17
|
assert.equal(r.status, 0);
|
|
18
18
|
const out = r.stdout;
|
|
19
19
|
assert.match(out, new RegExp(`Gipity CLI\\s+v${PKG_VERSION.replace(/\./g, '\\.')}`));
|
|
20
|
-
const sections = ['
|
|
20
|
+
const sections = ['Common:', 'Connect:', 'Project:', 'Files:', 'App building:', 'Utilities:', 'Agent:', 'Setup:'];
|
|
21
21
|
let lastIdx = -1;
|
|
22
22
|
for (const s of sections) {
|
|
23
23
|
const idx = out.indexOf(s);
|
|
@@ -26,11 +26,11 @@ describe('cli-smoke: --version and --help', () => {
|
|
|
26
26
|
lastIdx = idx;
|
|
27
27
|
}
|
|
28
28
|
});
|
|
29
|
-
it('--help lists doctor and update under
|
|
29
|
+
it('--help lists doctor and update under Setup', () => {
|
|
30
30
|
const r = runCli(['--help']);
|
|
31
|
-
const
|
|
32
|
-
assert.match(
|
|
33
|
-
assert.match(
|
|
31
|
+
const setup = r.stdout.split('Setup:')[1] ?? '';
|
|
32
|
+
assert.match(setup, /\bdoctor\b/);
|
|
33
|
+
assert.match(setup, /\bupdate\b/);
|
|
34
34
|
});
|
|
35
35
|
});
|
|
36
36
|
describe('cli-smoke: doctor', () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-smoke.test.js","sourceRoot":"","sources":["../../src/__tests__/cli-smoke.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEhD,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;AAE9G,QAAQ,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC/C,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC1B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,WAAW,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAChF,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC7B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAE1B,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC;QACrB,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,MAAM,CAAC,kBAAkB,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QAErF,MAAM,QAAQ,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"cli-smoke.test.js","sourceRoot":"","sources":["../../src/__tests__/cli-smoke.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,7 +21,7 @@ export interface SpawnOptions {
|
|
|
21
21
|
export declare function runCli(args: string[], opts?: SpawnOptions): SpawnResult;
|
|
22
22
|
export declare function makeTmpHome(): string;
|
|
23
23
|
/**
|
|
24
|
-
* Async version of runCli
|
|
24
|
+
* Async version of runCli - uses `spawn` instead of `spawnSync` so the
|
|
25
25
|
* test's event loop keeps turning while the child runs. Required for any
|
|
26
26
|
* test that spins up an in-process HTTP server for the child to hit
|
|
27
27
|
* (spawnSync deadlocks because the server can't accept connections while
|
|
@@ -4,7 +4,7 @@ import { dirname, resolve } from 'path';
|
|
|
4
4
|
import { mkdtempSync } from 'fs';
|
|
5
5
|
import { tmpdir } from 'os';
|
|
6
6
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
// __dirname is dist/__tests__/helpers
|
|
7
|
+
// __dirname is dist/__tests__/helpers - CLI entry is dist/index.js
|
|
8
8
|
export const CLI_ENTRY = resolve(__dirname, '..', '..', 'index.js');
|
|
9
9
|
/**
|
|
10
10
|
* Run the built gipity CLI with a deterministic, isolated environment.
|
|
@@ -37,7 +37,7 @@ export function makeTmpHome() {
|
|
|
37
37
|
return mkdtempSync(`${tmpdir()}/gipity-cli-test-`);
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
|
-
* Async version of runCli
|
|
40
|
+
* Async version of runCli - uses `spawn` instead of `spawnSync` so the
|
|
41
41
|
* test's event loop keeps turning while the child runs. Required for any
|
|
42
42
|
* test that spins up an in-process HTTP server for the child to hit
|
|
43
43
|
* (spawnSync deadlocks because the server can't accept connections while
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrap-format contract for relay-dispatched `gipity claude -p` messages.
|
|
3
|
+
*
|
|
4
|
+
* The user's actual message must sit between the `USER_MSG_OPEN` /
|
|
5
|
+
* `USER_MSG_CLOSE` tags with no trailing instructions, and the client-side
|
|
6
|
+
* `stripPreamble` in `platform/client/src/ts/commands/claude-display.ts`
|
|
7
|
+
* must use identical tag strings - otherwise the web CLI renders the full
|
|
8
|
+
* wrap as a `claude>` turn (the historical bug: duplicate user turns
|
|
9
|
+
* rendered as walls of preamble text).
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it } from 'node:test';
|
|
12
|
+
import assert from 'node:assert/strict';
|
|
13
|
+
import { readFileSync } from 'fs';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
import { dirname, resolve } from 'path';
|
|
16
|
+
import { USER_MSG_OPEN, USER_MSG_CLOSE, buildFreshWrap, buildResumeWrap, } from '../prompts.js';
|
|
17
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
// dist/__tests__/ → repo root depends on build layout. These tests also run
|
|
19
|
+
// against the source tree via tsx in CI - handle both by walking up until we
|
|
20
|
+
// find the platform/ sibling.
|
|
21
|
+
function repoRoot() {
|
|
22
|
+
let p = HERE;
|
|
23
|
+
for (let i = 0; i < 8; i++) {
|
|
24
|
+
try {
|
|
25
|
+
const probe = resolve(p, 'platform/client/src/ts/commands/claude-display.ts');
|
|
26
|
+
readFileSync(probe, 'utf-8');
|
|
27
|
+
return p;
|
|
28
|
+
}
|
|
29
|
+
catch { /* keep walking */ }
|
|
30
|
+
p = resolve(p, '..');
|
|
31
|
+
}
|
|
32
|
+
throw new Error('could not locate repo root from test file');
|
|
33
|
+
}
|
|
34
|
+
describe('buildFreshWrap', () => {
|
|
35
|
+
it('wraps the user message between USER_MSG_OPEN / USER_MSG_CLOSE', () => {
|
|
36
|
+
const out = buildFreshWrap('## Project context\nstuff here', 'hello world');
|
|
37
|
+
assert.ok(out.includes(USER_MSG_OPEN), 'missing open tag');
|
|
38
|
+
assert.ok(out.includes(USER_MSG_CLOSE), 'missing close tag');
|
|
39
|
+
const open = out.indexOf(USER_MSG_OPEN) + USER_MSG_OPEN.length;
|
|
40
|
+
const close = out.lastIndexOf(USER_MSG_CLOSE);
|
|
41
|
+
assert.equal(out.slice(open, close).trim(), 'hello world');
|
|
42
|
+
});
|
|
43
|
+
it('places the response directive before the user message, not after', () => {
|
|
44
|
+
const out = buildFreshWrap('ctx', 'do a thing');
|
|
45
|
+
const directiveIdx = out.indexOf(`Don't greet`);
|
|
46
|
+
const openIdx = out.indexOf(USER_MSG_OPEN);
|
|
47
|
+
assert.ok(directiveIdx !== -1, 'directive should be present');
|
|
48
|
+
assert.ok(directiveIdx < openIdx, 'directive must come before user-message tag');
|
|
49
|
+
});
|
|
50
|
+
it('has nothing after the closing tag', () => {
|
|
51
|
+
const out = buildFreshWrap('ctx', 'msg');
|
|
52
|
+
assert.ok(out.trimEnd().endsWith(USER_MSG_CLOSE), `wrap should end with close tag, got tail: ${out.slice(-120)}`);
|
|
53
|
+
});
|
|
54
|
+
it('does not emit the legacy "Answer directly" trailer', () => {
|
|
55
|
+
const out = buildFreshWrap('ctx', 'msg');
|
|
56
|
+
assert.equal(out.includes('Answer directly'), false, 'legacy trailer leaked into new wrap');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('buildResumeWrap', () => {
|
|
60
|
+
const opts = {
|
|
61
|
+
projectName: 'proj',
|
|
62
|
+
projectSlug: 'proj',
|
|
63
|
+
projectGuid: 'p_abc',
|
|
64
|
+
accountSlug: 'acct',
|
|
65
|
+
cwd: '/tmp',
|
|
66
|
+
};
|
|
67
|
+
it('wraps the user message between tags', () => {
|
|
68
|
+
const out = buildResumeWrap(opts, 'whats 2+2');
|
|
69
|
+
const open = out.indexOf(USER_MSG_OPEN) + USER_MSG_OPEN.length;
|
|
70
|
+
const close = out.lastIndexOf(USER_MSG_CLOSE);
|
|
71
|
+
assert.ok(open > USER_MSG_OPEN.length - 1, 'open tag missing');
|
|
72
|
+
assert.ok(close > open, 'close tag missing or before open');
|
|
73
|
+
assert.equal(out.slice(open, close).trim(), 'whats 2+2');
|
|
74
|
+
});
|
|
75
|
+
it('has nothing after the closing tag', () => {
|
|
76
|
+
const out = buildResumeWrap(opts, 'x');
|
|
77
|
+
assert.ok(out.trimEnd().endsWith(USER_MSG_CLOSE));
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe('tag constant drift guard', () => {
|
|
81
|
+
it('client-side claude-display.ts uses identical tag strings', () => {
|
|
82
|
+
const root = repoRoot();
|
|
83
|
+
const clientSrc = readFileSync(resolve(root, 'platform/client/src/ts/commands/claude-display.ts'), 'utf-8');
|
|
84
|
+
const openMatch = clientSrc.match(/USER_MSG_OPEN\s*=\s*'([^']+)'/);
|
|
85
|
+
const closeMatch = clientSrc.match(/USER_MSG_CLOSE\s*=\s*'([^']+)'/);
|
|
86
|
+
assert.ok(openMatch, 'claude-display.ts must define USER_MSG_OPEN as a single-quoted string literal');
|
|
87
|
+
assert.ok(closeMatch, 'claude-display.ts must define USER_MSG_CLOSE as a single-quoted string literal');
|
|
88
|
+
assert.equal(openMatch[1], USER_MSG_OPEN, 'open tag drift');
|
|
89
|
+
assert.equal(closeMatch[1], USER_MSG_CLOSE, 'close tag drift');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
// Local replica of the client-side stripPreamble. The client file can't be
|
|
93
|
+
// imported directly (different package, no DOM shim in node:test), so this
|
|
94
|
+
// replica + the drift-guard above (identical tag strings) gives equivalent
|
|
95
|
+
// coverage. If the algorithm shape changes in claude-display.ts, update
|
|
96
|
+
// both here and there.
|
|
97
|
+
function stripPreambleReplica(s) {
|
|
98
|
+
if (!s)
|
|
99
|
+
return s;
|
|
100
|
+
const m1 = s.match(/The user's first message:\s*"([\s\S]+?)"(?:\s*\n|\s*$)/);
|
|
101
|
+
if (m1)
|
|
102
|
+
return m1[1];
|
|
103
|
+
const open = s.indexOf(USER_MSG_OPEN);
|
|
104
|
+
const close = s.lastIndexOf(USER_MSG_CLOSE);
|
|
105
|
+
if (open !== -1 && close !== -1 && close > open) {
|
|
106
|
+
return s.slice(open + USER_MSG_OPEN.length, close).trim();
|
|
107
|
+
}
|
|
108
|
+
return s;
|
|
109
|
+
}
|
|
110
|
+
describe('stripPreamble round-trip', () => {
|
|
111
|
+
it('recovers the exact user message from buildFreshWrap output', () => {
|
|
112
|
+
const msg = 'hello world - whats 2+2 and also a newline\nplease';
|
|
113
|
+
const out = buildFreshWrap('## ctx\n- Name: foo\n- Files: empty', msg);
|
|
114
|
+
assert.equal(stripPreambleReplica(out), msg);
|
|
115
|
+
});
|
|
116
|
+
it('recovers the exact user message from buildResumeWrap output', () => {
|
|
117
|
+
const msg = 'resume test message';
|
|
118
|
+
const out = buildResumeWrap({ projectName: 'p', projectSlug: 'p', projectGuid: 'p_abc', accountSlug: 'a', cwd: '/' }, msg);
|
|
119
|
+
assert.equal(stripPreambleReplica(out), msg);
|
|
120
|
+
});
|
|
121
|
+
it('still handles the legacy first-message bootstrap form', () => {
|
|
122
|
+
const s = `Some context\n\nThe user's first message: "build a pacman game"\n\nGet started.`;
|
|
123
|
+
assert.equal(stripPreambleReplica(s), 'build a pacman game');
|
|
124
|
+
});
|
|
125
|
+
it('returns the input unchanged when no tags are present', () => {
|
|
126
|
+
assert.equal(stripPreambleReplica('plain message'), 'plain message');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
//# sourceMappingURL=prompts.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.test.js","sourceRoot":"","sources":["../../src/__tests__/prompts.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `gipity push` (pushFile) CAS behavior: when the local baseline is stale,
|
|
3
|
+
* the server returns 409 and we surface a clear error that tells the user
|
|
4
|
+
* to run `gipity sync`.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, before, after } from 'node:test';
|
|
7
|
+
import assert from 'node:assert/strict';
|
|
8
|
+
import { writeFileSync, mkdirSync, mkdtempSync, rmSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { tmpdir } from 'os';
|
|
11
|
+
// Set HOME BEFORE importing the CLI so ~/.gipity/auth.json resolves to our tempdir.
|
|
12
|
+
let home;
|
|
13
|
+
let projectDir;
|
|
14
|
+
let origHome;
|
|
15
|
+
let origCwd;
|
|
16
|
+
let origFetch;
|
|
17
|
+
before(() => {
|
|
18
|
+
origHome = process.env.HOME;
|
|
19
|
+
origCwd = process.cwd();
|
|
20
|
+
origFetch = globalThis.fetch;
|
|
21
|
+
home = mkdtempSync(join(tmpdir(), 'gipity-pushcas-home-'));
|
|
22
|
+
process.env.HOME = home;
|
|
23
|
+
mkdirSync(join(home, '.gipity'), { recursive: true });
|
|
24
|
+
writeFileSync(join(home, '.gipity', 'auth.json'), JSON.stringify({
|
|
25
|
+
accessToken: 'fake-jwt',
|
|
26
|
+
refreshToken: 'fake-refresh',
|
|
27
|
+
email: 'ec-test@914-6.com',
|
|
28
|
+
expiresAt: new Date(Date.now() + 10 * 60_000).toISOString(),
|
|
29
|
+
}));
|
|
30
|
+
projectDir = mkdtempSync(join(tmpdir(), 'gipity-pushcas-proj-'));
|
|
31
|
+
writeFileSync(join(projectDir, '.gipity.json'), JSON.stringify({
|
|
32
|
+
projectGuid: 'proj_test',
|
|
33
|
+
projectSlug: 'p',
|
|
34
|
+
accountSlug: 'a',
|
|
35
|
+
agentGuid: 'agt_t',
|
|
36
|
+
conversationGuid: null,
|
|
37
|
+
apiBase: 'https://test.invalid',
|
|
38
|
+
ignore: [],
|
|
39
|
+
}));
|
|
40
|
+
mkdirSync(join(projectDir, '.gipity'), { recursive: true });
|
|
41
|
+
process.chdir(projectDir);
|
|
42
|
+
});
|
|
43
|
+
after(() => {
|
|
44
|
+
process.chdir(origCwd);
|
|
45
|
+
globalThis.fetch = origFetch;
|
|
46
|
+
if (origHome === undefined)
|
|
47
|
+
delete process.env.HOME;
|
|
48
|
+
else
|
|
49
|
+
process.env.HOME = origHome;
|
|
50
|
+
rmSync(home, { recursive: true, force: true });
|
|
51
|
+
rmSync(projectDir, { recursive: true, force: true });
|
|
52
|
+
});
|
|
53
|
+
function stubFetch(handler) {
|
|
54
|
+
globalThis.fetch = handler;
|
|
55
|
+
}
|
|
56
|
+
describe('pushFile CAS', () => {
|
|
57
|
+
it('surfaces a clear error when the remote has a newer version', async () => {
|
|
58
|
+
// Baseline says we last synced at serverVersion=5.
|
|
59
|
+
writeFileSync(join(projectDir, '.gipity', 'sync-state.json'), JSON.stringify({
|
|
60
|
+
projectGuid: 'proj_test',
|
|
61
|
+
files: {
|
|
62
|
+
'hello.txt': {
|
|
63
|
+
size: 5, mtime: '2024-01-01T00:00:00.000Z',
|
|
64
|
+
sha256: 'fakesha-old',
|
|
65
|
+
serverVersion: 5,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
lastFullSync: '2024-01-01T00:00:00.000Z',
|
|
69
|
+
}));
|
|
70
|
+
writeFileSync(join(projectDir, 'hello.txt'), 'world');
|
|
71
|
+
// Server returns 409: it's at version 7 now, client expected 5.
|
|
72
|
+
stubFetch(async (url) => {
|
|
73
|
+
if (url.includes('/files/upload-init')) {
|
|
74
|
+
return new Response(JSON.stringify({
|
|
75
|
+
error: { code: 'CONFLICT', message: 'Version mismatch' },
|
|
76
|
+
data: { current_server_version: 7 },
|
|
77
|
+
}), { status: 409, headers: { 'content-type': 'application/json' } });
|
|
78
|
+
}
|
|
79
|
+
throw new Error(`unexpected fetch to ${url}`);
|
|
80
|
+
});
|
|
81
|
+
// Force-evict the module cache so our stubbed fetch is picked up cleanly.
|
|
82
|
+
const { clearConfigCache } = await import('../config.js');
|
|
83
|
+
clearConfigCache();
|
|
84
|
+
const { pushFile } = await import('../sync.js');
|
|
85
|
+
await assert.rejects(() => pushFile(join(projectDir, 'hello.txt')), /newer version.*serverVersion=7.*gipity sync/is);
|
|
86
|
+
});
|
|
87
|
+
it('succeeds when baseline matches - pushFile updates serverVersion in baseline', async () => {
|
|
88
|
+
writeFileSync(join(projectDir, '.gipity', 'sync-state.json'), JSON.stringify({
|
|
89
|
+
projectGuid: 'proj_test',
|
|
90
|
+
files: {
|
|
91
|
+
'greet.txt': {
|
|
92
|
+
size: 5, mtime: '2024-01-01T00:00:00.000Z',
|
|
93
|
+
sha256: 'fakesha-old',
|
|
94
|
+
serverVersion: 3,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
lastFullSync: '2024-01-01T00:00:00.000Z',
|
|
98
|
+
}));
|
|
99
|
+
writeFileSync(join(projectDir, 'greet.txt'), 'hi-v4');
|
|
100
|
+
// Server: upload-init → presigned URL. PUT to presigned URL → etag.
|
|
101
|
+
// upload-complete → 200 with server_version=4.
|
|
102
|
+
stubFetch(async (url, init) => {
|
|
103
|
+
if (url.includes('/files/upload-init')) {
|
|
104
|
+
return new Response(JSON.stringify({
|
|
105
|
+
data: {
|
|
106
|
+
upload_guid: 'fl_abc',
|
|
107
|
+
method: 'PUT',
|
|
108
|
+
url: 'https://s3.example/stage',
|
|
109
|
+
expires_in: 3600,
|
|
110
|
+
},
|
|
111
|
+
}), { status: 200, headers: { 'content-type': 'application/json' } });
|
|
112
|
+
}
|
|
113
|
+
if (url.startsWith('https://s3.example')) {
|
|
114
|
+
// Presigned PUT - return an etag.
|
|
115
|
+
return new Response('', { status: 200, headers: { etag: '"fake-etag"' } });
|
|
116
|
+
}
|
|
117
|
+
if (url.includes('/files/upload-complete')) {
|
|
118
|
+
return new Response(JSON.stringify({
|
|
119
|
+
data: { size: 5, guid: 'fl_new', version: 1, server_version: 4 },
|
|
120
|
+
}), { status: 200, headers: { 'content-type': 'application/json' } });
|
|
121
|
+
}
|
|
122
|
+
throw new Error(`unexpected fetch to ${url}`);
|
|
123
|
+
});
|
|
124
|
+
const { clearConfigCache } = await import('../config.js');
|
|
125
|
+
clearConfigCache();
|
|
126
|
+
const { pushFile, readBaseline } = await import('../sync.js');
|
|
127
|
+
await pushFile(join(projectDir, 'greet.txt'));
|
|
128
|
+
const bl = readBaseline('proj_test');
|
|
129
|
+
assert.ok(bl.files['greet.txt'], 'greet.txt should be in baseline');
|
|
130
|
+
assert.equal(bl.files['greet.txt'].serverVersion, 4, 'baseline should reflect bumped server version');
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
//# sourceMappingURL=push-cas.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push-cas.test.js","sourceRoot":"","sources":["../../src/__tests__/push-cas.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAE5B,oFAAoF;AACpF,IAAI,IAAY,CAAC;AACjB,IAAI,UAAkB,CAAC;AACvB,IAAI,QAA4B,CAAC;AACjC,IAAI,OAAe,CAAC;AACpB,IAAI,SAAkC,CAAC;AAEvC,MAAM,CAAC,GAAG,EAAE;IACV,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;IAC5B,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IACxB,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC;IAE7B,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,sBAAsB,CAAC,CAAC,CAAC;IAC3D,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAExB,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC;QAC/D,WAAW,EAAE,UAAU;QACvB,YAAY,EAAE,cAAc;QAC5B,KAAK,EAAE,mBAAmB;QAC1B,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC,WAAW,EAAE;KAC5D,CAAC,CAAC,CAAC;IAEJ,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,sBAAsB,CAAC,CAAC,CAAC;IACjE,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC;QAC7D,WAAW,EAAE,WAAW;QACxB,WAAW,EAAE,GAAG;QAChB,WAAW,EAAE,GAAG;QAChB,SAAS,EAAE,OAAO;QAClB,gBAAgB,EAAE,IAAI;QACtB,OAAO,EAAE,sBAAsB;QAC/B,MAAM,EAAE,EAAE;KACX,CAAC,CAAC,CAAC;IACJ,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AAC5B,CAAC,CAAC,CAAC;AAEH,KAAK,CAAC,GAAG,EAAE;IACT,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACvB,UAAU,CAAC,KAAK,GAAG,SAAS,CAAC;IAC7B,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;;QAC/C,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC;IACjC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,MAAM,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACvD,CAAC,CAAC,CAAC;AAEH,SAAS,SAAS,CAAC,OAA+D;IAChF,UAAU,CAAC,KAAK,GAAG,OAA6C,CAAC;AACnE,CAAC;AAED,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC1E,mDAAmD;QACnD,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,iBAAiB,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC;YAC3E,WAAW,EAAE,WAAW;YACxB,KAAK,EAAE;gBACL,WAAW,EAAE;oBACX,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,0BAA0B;oBAC1C,MAAM,EAAE,aAAa;oBACrB,aAAa,EAAE,CAAC;iBACjB;aACF;YACD,YAAY,EAAE,0BAA0B;SACzC,CAAC,CAAC,CAAC;QACJ,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;QAEtD,gEAAgE;QAChE,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YACtB,IAAI,GAAG,CAAC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,CAAC;gBACvC,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;oBACb,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,kBAAkB,EAAE;oBACxD,IAAI,EAAE,EAAE,sBAAsB,EAAE,CAAC,EAAE;iBACpC,CAAC,EACF,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CACjE,CAAC;YACJ,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,0EAA0E;QAC1E,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;QAC1D,gBAAgB,EAAE,CAAC;QACnB,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;QAEhD,MAAM,MAAM,CAAC,OAAO,CAClB,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,EAC7C,+CAA+C,CAChD,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE;QAC3F,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,iBAAiB,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC;YAC3E,WAAW,EAAE,WAAW;YACxB,KAAK,EAAE;gBACL,WAAW,EAAE;oBACX,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,0BAA0B;oBAC1C,MAAM,EAAE,aAAa;oBACrB,aAAa,EAAE,CAAC;iBACjB;aACF;YACD,YAAY,EAAE,0BAA0B;SACzC,CAAC,CAAC,CAAC;QACJ,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;QAEtD,oEAAoE;QACpE,+CAA+C;QAC/C,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YAC5B,IAAI,GAAG,CAAC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,CAAC;gBACvC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;oBACjC,IAAI,EAAE;wBACJ,WAAW,EAAE,QAAQ;wBACrB,MAAM,EAAE,KAAK;wBACb,GAAG,EAAE,0BAA0B;wBAC/B,UAAU,EAAE,IAAI;qBACjB;iBACF,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;YACxE,CAAC;YACD,IAAI,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC,EAAE,CAAC;gBACzC,kCAAkC;gBAClC,OAAO,IAAI,QAAQ,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC;YAC7E,CAAC;YACD,IAAI,GAAG,CAAC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,CAAC;gBAC3C,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;oBACjC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE;iBACjE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;YACxE,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;QAC1D,gBAAgB,EAAE,CAAC;QACnB,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;QAE9D,MAAM,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;QAE9C,MAAM,EAAE,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;QACrC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,iCAAiC,CAAC,CAAC;QACpE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,aAAa,EAAE,CAAC,EAAE,+CAA+C,CAAC,CAAC;IACxG,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -29,7 +29,7 @@ describe('bridgeAbort', () => {
|
|
|
29
29
|
assert.equal(inner.signal.aborted, true);
|
|
30
30
|
assert.equal(inner.signal.reason, 'pre');
|
|
31
31
|
});
|
|
32
|
-
it('detaches cleanly
|
|
32
|
+
it('detaches cleanly - does not leak listeners across many calls', () => {
|
|
33
33
|
const outer = new AbortController();
|
|
34
34
|
// Sanity: listenerCount via public API isn't part of EventTarget, so we
|
|
35
35
|
// verify no leak by running many cycles and confirming the process does
|
|
@@ -52,7 +52,7 @@ describe('bridgeAbort', () => {
|
|
|
52
52
|
assert.equal(leakWarn, undefined, `leaked listeners: ${leakWarn}`);
|
|
53
53
|
// After all detaches, firing outer.abort must not affect any prior
|
|
54
54
|
// inners (they're out of scope / already detached). Create one more
|
|
55
|
-
// inner WITHOUT detaching and confirm it receives the abort
|
|
55
|
+
// inner WITHOUT detaching and confirm it receives the abort - proves
|
|
56
56
|
// the bridge still works after many attach/detach cycles.
|
|
57
57
|
const liveInner = new AbortController();
|
|
58
58
|
bridgeAbort(outer.signal, liveInner);
|