gipity 1.1.4 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adopt-cwd.js +1 -1
- package/dist/banner.js +3 -5
- package/dist/commands/add.js +1 -1
- package/dist/commands/build.js +12 -11
- package/dist/commands/chat.js +1 -1
- package/dist/commands/deploy.js +1 -1
- package/dist/commands/generate.js +5 -5
- package/dist/commands/gmail.js +1 -1
- package/dist/commands/job.js +10 -5
- package/dist/commands/load.js +2 -2
- package/dist/commands/login.js +7 -2
- package/dist/commands/page-eval.js +5 -5
- package/dist/commands/page-fetch.js +14 -10
- package/dist/commands/page-inspect.js +2 -2
- package/dist/commands/page-screenshot.js +5 -5
- package/dist/commands/page-test.js +1 -1
- package/dist/commands/remove.js +1 -1
- package/dist/commands/sandbox.js +1 -1
- package/dist/commands/save.js +1 -1
- package/dist/commands/secrets.js +1 -1
- package/dist/commands/service.js +1 -1
- package/dist/commands/setup.js +4 -6
- package/dist/commands/status.js +41 -12
- package/dist/commands/storage.js +2 -2
- package/dist/commands/sync.js +15 -0
- package/dist/commands/test.js +1 -1
- package/dist/commands/upload.js +1 -1
- package/dist/commands/workflow.js +1 -1
- package/dist/index.js +495 -415
- package/dist/knowledge.js +3 -0
- package/dist/login-flow.js +44 -2
- package/dist/relay/onboarding.js +26 -44
- package/dist/sync.js +6 -6
- package/dist/utils.js +60 -3
- package/package.json +2 -2
package/dist/commands/status.js
CHANGED
|
@@ -4,7 +4,7 @@ import { join, resolve } from 'path';
|
|
|
4
4
|
import { homedir } from 'os';
|
|
5
5
|
import { getAuth, sessionExpired } from '../auth.js';
|
|
6
6
|
import { get, usingEnvToken, ApiError } from '../api.js';
|
|
7
|
-
import { getConfig, liveUrl } from '../config.js';
|
|
7
|
+
import { getConfig, liveUrl, resolveApiBase } from '../config.js';
|
|
8
8
|
import { brand, success, warning, muted, error as clrError } from '../colors.js';
|
|
9
9
|
import { GIPITY_PLUGIN_ID, GIPITY_MARKETPLACE_NAME, setupClaudeHooks, ensureGipityPlugin, ensureGipityPluginInstalled, userScopeInstallState } from '../setup.js';
|
|
10
10
|
import { flushBugQueue } from '../bug-queue.js';
|
|
@@ -39,15 +39,20 @@ function checkGipityPlugin() {
|
|
|
39
39
|
const stale = missing.length === 1 && missing[0] === 'install' && install.exists;
|
|
40
40
|
return { missing, ok: missing.length === 0, stale };
|
|
41
41
|
}
|
|
42
|
+
/** `account` is the server's `account_slug` for the authenticated identity
|
|
43
|
+
* (only populated on 'ok' - the /users/me call that proved the probe also
|
|
44
|
+
* returns it) - used for the ownership cross-check against the locally
|
|
45
|
+
* cached project (bug cli#S2: a wrong-account session used to read as
|
|
46
|
+
* fully healthy until a later command 404'd). */
|
|
42
47
|
async function probeAuth(loggedIn) {
|
|
43
48
|
if (!loggedIn && !usingEnvToken())
|
|
44
|
-
return 'none';
|
|
49
|
+
return { state: 'none', account: null };
|
|
45
50
|
if (!usingEnvToken() && sessionExpired())
|
|
46
|
-
return 'expired';
|
|
51
|
+
return { state: 'expired', account: null };
|
|
47
52
|
// Cap the probe well below the API layer's 60s request timeout - status is
|
|
48
53
|
// a diagnostic command and must answer fast even when the network is dark.
|
|
49
|
-
const timeout = new Promise(res => setTimeout(() => res('unreachable'), 5000).unref?.());
|
|
50
|
-
const call = get('/users/me').then(() => 'ok', (err) => (err instanceof ApiError && err.statusCode === 401 ? 'rejected' : 'unreachable'));
|
|
54
|
+
const timeout = new Promise(res => setTimeout(() => res({ state: 'unreachable', account: null }), 5000).unref?.());
|
|
55
|
+
const call = get('/users/me').then((res) => ({ state: 'ok', account: res.data?.accountSlug ?? null }), (err) => ({ state: (err instanceof ApiError && err.statusCode === 401 ? 'rejected' : 'unreachable'), account: null }));
|
|
51
56
|
return Promise.race([call, timeout]);
|
|
52
57
|
}
|
|
53
58
|
// `whoami` is the name agents reach for first when they want the signed-in
|
|
@@ -71,6 +76,12 @@ export const statusCommand = new Command('status')
|
|
|
71
76
|
// is empty, which is the common case, so this stays a no-op most runs.
|
|
72
77
|
const queueDelivered = (auth && !sessionExpired()) ? await flushBugQueue().catch(() => 0) : 0;
|
|
73
78
|
const probe = await probeAuth(!!auth);
|
|
79
|
+
// RBAC lets a project be shared to a collaborator whose own account
|
|
80
|
+
// legitimately differs from the project owner's - so this is advisory,
|
|
81
|
+
// never a hard error. Only meaningful once a live 'ok' call has actually
|
|
82
|
+
// returned an account to compare against config.accountSlug.
|
|
83
|
+
const accountMismatch = probe.state === 'ok' && !!config && !!probe.account && probe.account !== config.accountSlug;
|
|
84
|
+
const apiBaseInUse = resolveApiBase();
|
|
74
85
|
if (opts.json) {
|
|
75
86
|
console.log(JSON.stringify({
|
|
76
87
|
project: config ? {
|
|
@@ -78,17 +89,21 @@ export const statusCommand = new Command('status')
|
|
|
78
89
|
slug: config.projectSlug,
|
|
79
90
|
account: config.accountSlug,
|
|
80
91
|
apiBase: config.apiBase,
|
|
92
|
+
apiBaseInUse,
|
|
81
93
|
url: liveUrl(config),
|
|
82
94
|
} : null,
|
|
83
95
|
// `valid` reflects the refresh token (the real session) - access
|
|
84
96
|
// tokens auto-renew, so their expiry must not read as "invalid".
|
|
85
97
|
// `probe` is what one live call just proved: 'rejected' means every
|
|
86
98
|
// authenticated command will fail even though `valid` reads true.
|
|
99
|
+
// 'mismatch' overrides 'ok' when the live account isn't the one that
|
|
100
|
+
// owns this project - `valid` still reads true (the token IS valid).
|
|
87
101
|
auth: (auth || usingEnvToken()) ? {
|
|
88
102
|
email: auth?.email,
|
|
103
|
+
account: probe.account,
|
|
89
104
|
source: usingEnvToken() ? 'agent-token' : 'session',
|
|
90
|
-
valid: usingEnvToken() ? probe !== 'rejected' : !sessionExpired(),
|
|
91
|
-
probe,
|
|
105
|
+
valid: usingEnvToken() ? probe.state !== 'rejected' : !sessionExpired(),
|
|
106
|
+
probe: accountMismatch ? 'mismatch' : probe.state,
|
|
92
107
|
} : null,
|
|
93
108
|
plugin: hookCheck,
|
|
94
109
|
}, null, 2));
|
|
@@ -102,28 +117,42 @@ export const statusCommand = new Command('status')
|
|
|
102
117
|
console.log(`${muted('Account:')} ${config.accountSlug}`);
|
|
103
118
|
console.log(`${muted('Live:')} ${liveUrl(config)}`);
|
|
104
119
|
console.log(`${muted('API:')} ${config.apiBase}`);
|
|
120
|
+
// apiBase is only what THIS project recorded - resolveApiBase() is what
|
|
121
|
+
// every real request actually uses (it can diverge via GIPITY_API_BASE,
|
|
122
|
+
// --api-base, or a disallowed host being dropped to the default). Surface
|
|
123
|
+
// the divergence rather than silently trusting the recorded value.
|
|
124
|
+
if (apiBaseInUse !== config.apiBase) {
|
|
125
|
+
console.log(`${muted('API (in use):')} ${warning(apiBaseInUse)} ${muted('(overrides .gipity.json — GIPITY_API_BASE / --api-base / allowlist)')}`);
|
|
126
|
+
}
|
|
105
127
|
if (config.agentGuid)
|
|
106
128
|
console.log(`${muted('Agent:')} ${config.agentGuid}`);
|
|
107
129
|
}
|
|
108
130
|
if (usingEnvToken()) {
|
|
109
|
-
console.log(`${muted('Auth:')} ${probe === 'rejected'
|
|
131
|
+
console.log(`${muted('Auth:')} ${probe.state === 'rejected'
|
|
110
132
|
? warning('agent API token (GIPITY_TOKEN) rejected by the server — mint a new one: gipity skill read agent-deploy')
|
|
111
|
-
: success('agent API token (GIPITY_TOKEN)')}${probe === 'unreachable' ? ` ${muted('(unverified — API unreachable)')}` : ''}`);
|
|
133
|
+
: success('agent API token (GIPITY_TOKEN)')}${probe.state === 'unreachable' ? ` ${muted('(unverified — API unreachable)')}` : ''}`);
|
|
112
134
|
}
|
|
113
135
|
else if (!auth) {
|
|
114
136
|
console.log(`${muted('Auth:')} ${warning('not logged in. Run: gipity login')}`);
|
|
115
137
|
}
|
|
116
|
-
else if (probe === 'expired') {
|
|
138
|
+
else if (probe.state === 'expired') {
|
|
117
139
|
console.log(`${muted('Auth:')} ${warning(`session expired for ${auth.email}. Run: gipity login (headless/CI: set GIPITY_TOKEN — gipity skill read agent-deploy)`)}`);
|
|
118
140
|
}
|
|
119
|
-
else if (probe === 'rejected') {
|
|
141
|
+
else if (probe.state === 'rejected') {
|
|
120
142
|
// Locally fresh but the server says no (refresh token rotated away or
|
|
121
143
|
// revoked). Without the live probe this printed a green identity while
|
|
122
144
|
// every authenticated command failed.
|
|
123
145
|
console.log(`${muted('Auth:')} ${warning(`session for ${auth.email} was rejected by the server. Run: gipity login (headless/CI: set GIPITY_TOKEN — gipity skill read agent-deploy)`)}`);
|
|
124
146
|
}
|
|
125
147
|
else {
|
|
126
|
-
console.log(`${muted('Auth:')} ${success(auth.email)}${probe === 'unreachable' ? ` ${muted('(unverified — API unreachable)')}` : ''}`);
|
|
148
|
+
console.log(`${muted('Auth:')} ${success(auth.email)}${probe.state === 'unreachable' ? ` ${muted('(unverified — API unreachable)')}` : ''}`);
|
|
149
|
+
}
|
|
150
|
+
// Source-independent (session or GIPITY_TOKEN): a mismatch under an agent
|
|
151
|
+
// token is the same wrong-account class and must not be hidden inside the
|
|
152
|
+
// cascade above, which only special-cases 'rejected' for that source.
|
|
153
|
+
if (accountMismatch) {
|
|
154
|
+
console.log(`${muted('Account:')} ${warning(`logged-in account (${probe.account}) differs from this project's account (${config.accountSlug}). If you didn't expect this you may be logged into the wrong account — run: gipity login`)}`);
|
|
155
|
+
console.log(muted('(If this project was shared with you via gipity rbac, this is expected.)'));
|
|
127
156
|
}
|
|
128
157
|
if (queueDelivered > 0) {
|
|
129
158
|
console.log(`${muted('Bug queue:')} ${success(`delivered ${queueDelivered} queued bug report${queueDelivered === 1 ? '' : 's'}`)}`);
|
package/dist/commands/storage.js
CHANGED
|
@@ -50,12 +50,12 @@ function printUsage(d) {
|
|
|
50
50
|
console.log(`${bold('By project')} ${muted(scope)}`);
|
|
51
51
|
for (const p of d.projects) {
|
|
52
52
|
const name = p.projectName ?? '(no project)';
|
|
53
|
-
row(name.length > 16 ? `${name.slice(0,
|
|
53
|
+
row(name.length > 16 ? `${name.slice(0, 13)}...` : name, formatBytes(p.liveBytes), `${p.liveFiles.toLocaleString()} files`);
|
|
54
54
|
}
|
|
55
55
|
if (hidden > 0) {
|
|
56
56
|
const shownBytes = d.projects.reduce((n, p) => n + p.liveBytes, 0);
|
|
57
57
|
const shownFiles = d.projects.reduce((n, p) => n + p.liveFiles, 0);
|
|
58
|
-
row(
|
|
58
|
+
row(`...and ${hidden.toLocaleString()} more`, formatBytes(totals.liveBytes - shownBytes), `${(totals.liveFiles - shownFiles).toLocaleString()} files`);
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
const r = d.versionRetention;
|
package/dist/commands/sync.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
2
3
|
import { sync } from '../sync.js';
|
|
3
4
|
import { createProgressReporter } from '../progress.js';
|
|
4
5
|
import { error as clrError, muted } from '../colors.js';
|
|
6
|
+
import { findWindowsTwinProject, isWsl, wslPathToWindows } from '../utils.js';
|
|
7
|
+
import { getConfigPath } from '../config.js';
|
|
5
8
|
export const syncCommand = new Command('sync')
|
|
6
9
|
.description('Sync files')
|
|
7
10
|
.addHelpText('after', '\nAdd a .gipityignore at the project root to exclude paths (gitignore-style).')
|
|
@@ -30,6 +33,18 @@ export const syncCommand = new Command('sync')
|
|
|
30
33
|
// local). Surface that here as a helpful hint - not a red error - with
|
|
31
34
|
// the one command that resolves it. Only `gipity sync` shows this;
|
|
32
35
|
// deploy/test/sandbox stay silent (their internal sync defers quietly).
|
|
36
|
+
// WSL trap: a same-named folder under C:\Users\...\GipityProjects looks
|
|
37
|
+
// like "the project" from Windows Explorer, but files dropped there
|
|
38
|
+
// never sync. When a twin exists, say so - "Up to date." alone reads as
|
|
39
|
+
// "your new files are in" when they're sitting on the Windows side.
|
|
40
|
+
if (isWsl()) {
|
|
41
|
+
const configPath = getConfigPath();
|
|
42
|
+
const twin = configPath ? findWindowsTwinProject(dirname(configPath)) : null;
|
|
43
|
+
if (twin) {
|
|
44
|
+
console.log(muted(`\nNote: a Windows-side copy of this project exists at ${wslPathToWindows(twin)} and is NOT synced. ` +
|
|
45
|
+
`If you added files there, move them into this folder so they sync.`));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
33
48
|
if (result.deferredDeletes > 0) {
|
|
34
49
|
console.log(muted(`\n${result.deferredDeletes} file${result.deferredDeletes > 1 ? 's' : ''} on Gipity ` +
|
|
35
50
|
`${result.deferredDeletes > 1 ? 'are' : 'is'} not present locally and ${result.deferredDeletes > 1 ? 'were' : 'was'} left untouched. ` +
|
package/dist/commands/test.js
CHANGED
|
@@ -149,7 +149,7 @@ async function pollTestStatus(projectGuid, runGuid, opts) {
|
|
|
149
149
|
const tally = data.passed + data.failed > 0
|
|
150
150
|
? ` (${data.passed} passed${data.failed > 0 ? `, ${data.failed} failed` : ''} so far)`
|
|
151
151
|
: '';
|
|
152
|
-
console.log(muted(`
|
|
152
|
+
console.log(muted(` ... still running - ${progress}${tally}, ${elapsed}s elapsed`));
|
|
153
153
|
if (now - startTime >= LONG_RUN_MS && !longRunHintShown) {
|
|
154
154
|
longRunHintShown = true;
|
|
155
155
|
console.log(muted(' Note: progressing, not hung. LLM-backed tests can take minutes. To verify one function fast, use `gipity fn call <name>`; narrow this suite with `gipity test <path>`.'));
|
package/dist/commands/upload.js
CHANGED
|
@@ -65,7 +65,7 @@ token-signed serve url instead (reachable only by holders of the url).`)
|
|
|
65
65
|
};
|
|
66
66
|
const data = opts.json
|
|
67
67
|
? await doUpload()
|
|
68
|
-
: await withSpinner(`Uploading ${filename} (${formatSize(size)})
|
|
68
|
+
: await withSpinner(`Uploading ${filename} (${formatSize(size)})...`, doUpload, { done: null });
|
|
69
69
|
if (opts.json) {
|
|
70
70
|
console.log(JSON.stringify(data));
|
|
71
71
|
return;
|
|
@@ -186,7 +186,7 @@ workflowCommand
|
|
|
186
186
|
}));
|
|
187
187
|
workflowCommand
|
|
188
188
|
.command('runs <name> [runGuid]')
|
|
189
|
-
.description('List recent runs, or pass a run guid (wr_
|
|
189
|
+
.description('List recent runs, or pass a run guid (wr_...) to see that run\'s per-step outputs')
|
|
190
190
|
.option('--json', 'Output as JSON')
|
|
191
191
|
.action((name, runGuid, _opts, cmd) => run('Runs', async () => {
|
|
192
192
|
const opts = mergedOpts(cmd);
|