@zivis/cli 0.1.0-alpha.43 → 0.1.0-alpha.44
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/commands/auth/init.js +9 -5
- package/dist/commands/run/index.js +22 -0
- package/dist/internal/devx-run.d.ts +21 -0
- package/dist/internal/project-binding.js +2 -2
- package/dist/internal/resolve-init-session.d.ts +6 -4
- package/dist/internal/resolve-init-session.js +32 -6
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ import { discardOauthAfterLogin, tryAutoMintBindingTokenAfterLogin, } from "../.
|
|
|
9
9
|
import { decodeJwtPayload } from "@zivis/mcp/auth/jwt";
|
|
10
10
|
import { detectIdesInRepo, writeGuidance } from "../../internal/ide-setup.js";
|
|
11
11
|
import { buildLinkagePrompt } from "../../internal/github-linkage-prompt.js";
|
|
12
|
-
import {
|
|
12
|
+
import { resolveExistingSessionAuth } from "../../internal/resolve-init-session.js";
|
|
13
13
|
async function maybePromptGitHubLinkage(config) {
|
|
14
14
|
try {
|
|
15
15
|
const msg = await buildLinkagePrompt({ config });
|
|
@@ -74,11 +74,15 @@ export async function initCommand(opts) {
|
|
|
74
74
|
console.log(`Environment: ${envLabel} (${config.apiBaseUrl})`);
|
|
75
75
|
console.log("");
|
|
76
76
|
let tokens;
|
|
77
|
-
|
|
77
|
+
let existingOrgId;
|
|
78
|
+
let existingEmail;
|
|
79
|
+
const existing = await resolveExistingSessionAuth(config, session, process.cwd());
|
|
78
80
|
if (existing) {
|
|
79
81
|
console.log("Existing session found — reusing stored credentials.");
|
|
80
82
|
console.log("(Run `zivis auth logout` first to switch accounts.)\n");
|
|
81
|
-
tokens = existing;
|
|
83
|
+
tokens = { access_token: existing.accessToken, id_token: existing.idToken };
|
|
84
|
+
existingOrgId = existing.organizationId;
|
|
85
|
+
existingEmail = existing.email;
|
|
82
86
|
}
|
|
83
87
|
else {
|
|
84
88
|
console.log("Opening browser for authentication...");
|
|
@@ -91,9 +95,9 @@ export async function initCommand(opts) {
|
|
|
91
95
|
process.exit(1);
|
|
92
96
|
}
|
|
93
97
|
}
|
|
94
|
-
const email = extractEmail(tokens.id_token);
|
|
98
|
+
const email = existingEmail ?? extractEmail(tokens.id_token);
|
|
95
99
|
const claims = decodeJwtPayload(tokens.access_token);
|
|
96
|
-
const jwtOrgId = claims?.org_id;
|
|
100
|
+
const jwtOrgId = existingOrgId ?? claims?.org_id;
|
|
97
101
|
if (jwtOrgId) {
|
|
98
102
|
let orgName;
|
|
99
103
|
try {
|
|
@@ -45,17 +45,39 @@ export function registerRunCommands(program) {
|
|
|
45
45
|
contractVersion: DEVX_RUN_CONTRACT_VERSION,
|
|
46
46
|
result,
|
|
47
47
|
});
|
|
48
|
+
const reconciliation = response.reconciliation;
|
|
48
49
|
const output = jsonEnvelope("run complete", {
|
|
49
50
|
run_id: runId,
|
|
50
51
|
outcome: response.outcome,
|
|
51
52
|
status: response.run.status,
|
|
52
53
|
completed_at: response.run.completedAt,
|
|
54
|
+
...(reconciliation
|
|
55
|
+
? {
|
|
56
|
+
finding_ids: reconciliation.findings.map((f) => ({ id: f.id, action: f.action })),
|
|
57
|
+
retest_results: reconciliation.retests,
|
|
58
|
+
evidence_ids: reconciliation.evidence.map((e) => ({ id: e.id, action: e.action })),
|
|
59
|
+
coverage: reconciliation.coverage,
|
|
60
|
+
}
|
|
61
|
+
: {}),
|
|
53
62
|
});
|
|
54
63
|
if (json) {
|
|
55
64
|
printJson(output);
|
|
56
65
|
}
|
|
57
66
|
else {
|
|
58
67
|
console.log(`zivis run complete: ${response.outcome} (run ${runId} is now ${response.run.status})`);
|
|
68
|
+
if (reconciliation) {
|
|
69
|
+
const created = reconciliation.findings.filter((f) => f.action === "created").length;
|
|
70
|
+
const updated = reconciliation.findings.filter((f) => f.action === "updated").length;
|
|
71
|
+
const retested = reconciliation.retests.filter((r) => r.action === "disposition_updated").length;
|
|
72
|
+
if (created)
|
|
73
|
+
console.log(` + ${created} new finding${created === 1 ? "" : "s"}`);
|
|
74
|
+
if (updated)
|
|
75
|
+
console.log(` ~ ${updated} finding${updated === 1 ? "" : "s"} updated`);
|
|
76
|
+
if (retested)
|
|
77
|
+
console.log(` ✓ ${retested} retest${retested === 1 ? "" : "s"} recorded`);
|
|
78
|
+
if (reconciliation.evidence.length)
|
|
79
|
+
console.log(` ${reconciliation.evidence.length} artifact(s) persisted`);
|
|
80
|
+
}
|
|
59
81
|
}
|
|
60
82
|
process.exit(EXIT.OK);
|
|
61
83
|
}
|
|
@@ -31,9 +31,30 @@ export interface DevXRunWire {
|
|
|
31
31
|
cancelledAt: string | null;
|
|
32
32
|
cancelReason: string | null;
|
|
33
33
|
}
|
|
34
|
+
export interface DevXRunReconciliationSummary {
|
|
35
|
+
findings: Array<{
|
|
36
|
+
id: string;
|
|
37
|
+
action: "created" | "updated" | "unchanged";
|
|
38
|
+
fingerprint: string;
|
|
39
|
+
}>;
|
|
40
|
+
retests: Array<{
|
|
41
|
+
findingId: string;
|
|
42
|
+
outcome: "resolved" | "still_present";
|
|
43
|
+
action: "disposition_updated" | "no_change";
|
|
44
|
+
}>;
|
|
45
|
+
evidence: Array<{
|
|
46
|
+
id: string;
|
|
47
|
+
action: "created" | "existing";
|
|
48
|
+
}>;
|
|
49
|
+
coverage: Array<{
|
|
50
|
+
scopeId: string;
|
|
51
|
+
applicability: string;
|
|
52
|
+
}>;
|
|
53
|
+
}
|
|
34
54
|
export interface DevXRunOutcome {
|
|
35
55
|
outcome: "completed" | "cancelled" | "idempotent-replay";
|
|
36
56
|
run: DevXRunWire;
|
|
57
|
+
reconciliation?: DevXRunReconciliationSummary;
|
|
37
58
|
}
|
|
38
59
|
export declare function isRetryableDevXRunError(err: unknown): boolean;
|
|
39
60
|
export declare function completeDevXRunRemote(client: DevXRunApi, params: {
|
|
@@ -7,10 +7,10 @@ import { fetchUserOrgs } from "./orgs.js";
|
|
|
7
7
|
export async function ensureProjectBinding(opts) {
|
|
8
8
|
const { config, session, accessToken, cwd, autoBind, skipBind } = opts;
|
|
9
9
|
const claims = decodeJwtPayload(accessToken);
|
|
10
|
-
const jwtOrgId = claims?.org_id;
|
|
10
|
+
const jwtOrgId = claims?.org_id ?? opts.organizationId;
|
|
11
11
|
if (!jwtOrgId) {
|
|
12
12
|
console.log("");
|
|
13
|
-
console.log(" Note: JWT has no
|
|
13
|
+
console.log(" Note: no org_id available (JWT has none, and no organizationId was supplied) — skipping project binding.");
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
let detected = detectProjectBinding(cwd);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type ZivisConfig } from "@zivis/mcp/types";
|
|
2
|
-
export interface
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
export interface ExistingSessionAuth {
|
|
3
|
+
accessToken: string;
|
|
4
|
+
idToken?: string;
|
|
5
|
+
organizationId?: string;
|
|
6
|
+
email?: string;
|
|
5
7
|
}
|
|
6
|
-
export declare function
|
|
8
|
+
export declare function resolveExistingSessionAuth(config: ZivisConfig, session: string, cwd: string): Promise<ExistingSessionAuth | null>;
|
|
@@ -1,10 +1,36 @@
|
|
|
1
|
-
import { loadTokens } from "@zivis/mcp/auth";
|
|
1
|
+
import { loadTokens, loadSessionIndex, resolveAuth } from "@zivis/mcp/auth";
|
|
2
2
|
import { getValidAccessToken } from "@zivis/mcp/auth/token-refresh";
|
|
3
3
|
import { credentialStorageFromConfig } from "@zivis/mcp/types";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import { detectProjectBinding } from "@zivis/mcp/project-binding";
|
|
5
|
+
import { fetchUserOrgs } from "./orgs.js";
|
|
6
|
+
export async function resolveExistingSessionAuth(config, session, cwd) {
|
|
7
|
+
const sessionConfig = { ...config, session };
|
|
8
|
+
const oauthAccessToken = await getValidAccessToken(sessionConfig);
|
|
9
|
+
if (oauthAccessToken) {
|
|
10
|
+
const stored = await loadTokens(session, credentialStorageFromConfig(config));
|
|
11
|
+
return { accessToken: oauthAccessToken, idToken: stored?.id_token };
|
|
12
|
+
}
|
|
13
|
+
const creds = await resolveAuth(sessionConfig, "apikey");
|
|
14
|
+
if (creds.method !== "apikey" || !creds.apiKey)
|
|
7
15
|
return null;
|
|
8
|
-
const
|
|
9
|
-
|
|
16
|
+
const binding = detectProjectBinding(cwd);
|
|
17
|
+
const workosOrgId = binding?.binding.workosOrgId;
|
|
18
|
+
if (!workosOrgId)
|
|
19
|
+
return null;
|
|
20
|
+
let orgs;
|
|
21
|
+
try {
|
|
22
|
+
orgs = await fetchUserOrgs(config, creds.apiKey);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
if (!orgs.some((o) => o.workosOrgId === workosOrgId)) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const index = await loadSessionIndex(credentialStorageFromConfig(config));
|
|
31
|
+
return {
|
|
32
|
+
accessToken: creds.apiKey,
|
|
33
|
+
organizationId: workosOrgId,
|
|
34
|
+
email: index[session]?.email,
|
|
35
|
+
};
|
|
10
36
|
}
|
package/package.json
CHANGED