@gajae-code/coding-agent 0.18.7 → 0.18.8
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/CHANGELOG.md +279 -1
- package/dist/types/async/job-manager.d.ts +1 -1
- package/dist/types/capability/mcp.d.ts +2 -0
- package/dist/types/capability/skill.d.ts +2 -0
- package/dist/types/capability/types.d.ts +2 -0
- package/dist/types/cli/public-command-errors.d.ts +3 -1
- package/dist/types/cli/setup-cli.d.ts +1 -1
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/config/autorouting-tier-map.d.ts +348 -9
- package/dist/types/config/file-lock.d.ts +1 -0
- package/dist/types/config/model-bindings-applier.d.ts +2 -0
- package/dist/types/config/model-profile-activation.d.ts +11 -3
- package/dist/types/config/model-registry.d.ts +4 -9
- package/dist/types/config/skill-settings-defaults.d.ts +2 -0
- package/dist/types/cursor-delete-path.d.ts +1 -0
- package/dist/types/cursor-mcp-name.d.ts +8 -0
- package/dist/types/customization/inventory.d.ts +2 -0
- package/dist/types/debug/report-bundle-parent.d.ts +2 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +1 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.d.ts +31 -0
- package/dist/types/defaults/gjc-skills.generated.d.ts +5 -6
- package/dist/types/discovery/helpers.d.ts +6 -1
- package/dist/types/export/html/css-color.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +7 -0
- package/dist/types/extensibility/gjc-plugins/runtime-adapters.d.ts +14 -1
- package/dist/types/extensibility/plugins/marketplace/cache.d.ts +2 -0
- package/dist/types/extensibility/plugins/plugin-link-path.d.ts +2 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +3 -1
- package/dist/types/extensibility/skill-management.d.ts +3 -1
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-crystallize.d.ts +74 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +11 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
- package/dist/types/gjc-runtime/state-argv.d.ts +1 -1
- package/dist/types/gjc-runtime/state-migrations.d.ts +8 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +95 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +11 -1
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -0
- package/dist/types/internal-urls/skill-path.d.ts +1 -0
- package/dist/types/lsp/workspace-path.d.ts +1 -0
- package/dist/types/modes/acp/acp-agent.d.ts +7 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -0
- package/dist/types/modes/components/customization/customization-dashboard.d.ts +1 -0
- package/dist/types/modes/components/extensions/state-manager.d.ts +4 -3
- package/dist/types/modes/controllers/input-controller.d.ts +9 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/utils/interrupt-ownership.d.ts +3 -0
- package/dist/types/modes/utils/keybinding-matchers.d.ts +1 -0
- package/dist/types/modes/utils/macos-option-forwarding.d.ts +17 -0
- package/dist/types/progress/collect-project-progress.d.ts +38 -0
- package/dist/types/progress/progress-contract.d.ts +154 -0
- package/dist/types/progress/progress-snapshot.d.ts +11 -0
- package/dist/types/progress/project-progress.d.ts +141 -0
- package/dist/types/progress/render-progress.d.ts +28 -0
- package/dist/types/runtime-mcp/oauth-public-url.d.ts +1 -0
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
- package/dist/types/runtime-mcp/smithery-registry.d.ts +24 -0
- package/dist/types/runtime-mcp/types.d.ts +6 -0
- package/dist/types/sdk/acp/adapter.d.ts +1 -1
- package/dist/types/sdk/broker/broker-exit.d.ts +6 -2
- package/dist/types/sdk/broker/broker.d.ts +18 -1
- package/dist/types/sdk/broker/session-index.d.ts +12 -6
- package/dist/types/sdk/bus/index.d.ts +12 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -1
- package/dist/types/sdk/client/client.d.ts +2 -0
- package/dist/types/sdk/host/query/handlers.d.ts +3 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +2 -0
- package/dist/types/sdk/session-list.d.ts +1 -1
- package/dist/types/sdk/session.d.ts +8 -0
- package/dist/types/session/agent-session.d.ts +38 -9
- package/dist/types/session/artifacts.d.ts +2 -2
- package/dist/types/session/internal/managed-gc-retirement-codec.d.ts +32 -0
- package/dist/types/session/internal/managed-session-scope.d.ts +79 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +26 -1
- package/dist/types/session/internal/managed-task-owner-cleanup.d.ts +62 -0
- package/dist/types/session/internal/task-artifact-owner-access.d.ts +2 -0
- package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +9 -1
- package/dist/types/session/session-manager.d.ts +31 -5
- package/dist/types/session/session-retirement.d.ts +31 -7
- package/dist/types/session/session-storage.d.ts +62 -2
- package/dist/types/session/task-artifact-owner-codec.d.ts +2 -0
- package/dist/types/setup/credential-auto-import.d.ts +1 -1
- package/dist/types/setup/credential-import.d.ts +6 -4
- package/dist/types/skill-state/active-state.d.ts +38 -0
- package/dist/types/slash-commands/helpers/progress-report.d.ts +8 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/tools/bash-skill-urls.d.ts +1 -1
- package/dist/types/tools/index.d.ts +3 -0
- package/dist/types/tools/python.d.ts +9 -1
- package/dist/types/tools/recipe/recipe-guard.d.ts +7 -0
- package/dist/types/tools/restricted-cwd.d.ts +1 -0
- package/dist/types/tools/todo-write.d.ts +6 -0
- package/dist/types/utils/tools-checksum.d.ts +7 -0
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +21 -0
- package/scripts/verify-managed-empty-stop.ts +28 -0
- package/src/async/job-manager.ts +228 -122
- package/src/capability/index.ts +17 -0
- package/src/capability/mcp.ts +2 -0
- package/src/capability/skill.ts +2 -0
- package/src/capability/types.ts +2 -0
- package/src/cli/auth-broker-cli.ts +1 -1
- package/src/cli/customize-doctor.ts +15 -2
- package/src/cli/public-command-errors.ts +21 -1
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/skills-cli.ts +5 -0
- package/src/commands/deep-interview.ts +47 -8
- package/src/commands/sdk.ts +28 -8
- package/src/config/autorouting-tier-map.ts +117 -7
- package/src/config/file-lock.ts +307 -68
- package/src/config/mcp-schema.json +4 -0
- package/src/config/model-bindings-applier.ts +20 -0
- package/src/config/model-profile-activation.ts +240 -46
- package/src/config/model-registry.ts +47 -153
- package/src/config/skill-settings-defaults.ts +7 -0
- package/src/coordinator-mcp/policy.ts +3 -2
- package/src/cursor-delete-path.ts +10 -0
- package/src/cursor-mcp-name.ts +15 -0
- package/src/cursor.ts +13 -2
- package/src/customization/inventory.ts +3 -0
- package/src/debug/report-bundle-parent.ts +22 -0
- package/src/debug/report-bundle.ts +3 -0
- package/src/debug/system-info.ts +13 -1
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +67 -4
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.ts +80 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +105 -10
- package/src/defaults/gjc/skills/ralplan/SKILL.md +6 -4
- package/src/defaults/gjc-defaults.test.ts +18 -1
- package/src/defaults/gjc-defaults.ts +3 -0
- package/src/defaults/gjc-skills.generated.ts +58 -36
- package/src/discovery/agents.ts +6 -1
- package/src/discovery/builtin.ts +10 -0
- package/src/discovery/helpers.ts +159 -20
- package/src/discovery/mcp-json.ts +6 -0
- package/src/discovery/ssh.ts +1 -1
- package/src/discovery/substitute-plugin-root.ts +3 -1
- package/src/edit/file-read-cache.ts +1 -1
- package/src/eval/py/executor.ts +380 -120
- package/src/export/html/css-color.ts +9 -0
- package/src/export/html/index.ts +5 -4
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +7 -0
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +73 -8
- package/src/extensibility/plugins/manager.ts +3 -2
- package/src/extensibility/plugins/marketplace/cache.ts +15 -0
- package/src/extensibility/plugins/marketplace/manager.ts +11 -3
- package/src/extensibility/plugins/plugin-link-path.ts +12 -0
- package/src/extensibility/runtime-skill-discovery.ts +20 -2
- package/src/extensibility/skill-management.ts +6 -2
- package/src/extensibility/skills.ts +6 -1
- package/src/gjc-runtime/deep-interview-crystallize.ts +1814 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +20 -2
- package/src/gjc-runtime/deep-interview-runtime.ts +1415 -13
- package/src/gjc-runtime/deep-interview-stage.ts +98 -21
- package/src/gjc-runtime/gc-runtime.ts +206 -9
- package/src/gjc-runtime/launch-worktree.ts +14 -4
- package/src/gjc-runtime/ralplan-runtime.ts +447 -52
- package/src/gjc-runtime/state-argv.ts +2 -0
- package/src/gjc-runtime/state-migrations.ts +91 -29
- package/src/gjc-runtime/state-runtime.ts +3716 -135
- package/src/gjc-runtime/state-writer.ts +83 -8
- package/src/gjc-runtime/ultragoal-runtime.ts +16 -4
- package/src/gjc-runtime/workflow-manifest.generated.json +70 -42
- package/src/gjc-runtime/workflow-manifest.ts +54 -21
- package/src/harness-control-plane/operate.ts +12 -4
- package/src/harness-control-plane/owner.ts +5 -1
- package/src/hashline/parser.ts +48 -9
- package/src/hashline/recovery.ts +13 -4
- package/src/internal-urls/docs-index.generated.ts +18 -17
- package/src/internal-urls/skill-path.ts +19 -0
- package/src/internal-urls/skill-protocol.ts +2 -0
- package/src/lsp/edits.ts +6 -0
- package/src/lsp/workspace-path.ts +51 -0
- package/src/main.ts +195 -31
- package/src/modes/acp/acp-agent.ts +88 -4
- package/src/modes/components/custom-editor.ts +14 -0
- package/src/modes/components/customization/customization-dashboard.ts +12 -0
- package/src/modes/components/extensions/extension-dashboard.ts +2 -2
- package/src/modes/components/extensions/state-manager.ts +15 -5
- package/src/modes/components/model-selector.ts +1 -4
- package/src/modes/components/provider-onboarding-selector.ts +1 -1
- package/src/modes/components/status-line/segments.ts +4 -0
- package/src/modes/controllers/event-controller.ts +11 -4
- package/src/modes/controllers/input-controller.ts +74 -29
- package/src/modes/controllers/runtime-mcp-command-controller.ts +8 -3
- package/src/modes/controllers/selector-controller.ts +28 -15
- package/src/modes/interactive-mode.ts +4 -0
- package/src/modes/runtime-init.ts +4 -0
- package/src/modes/utils/abort-message.ts +3 -1
- package/src/modes/utils/interrupt-ownership.ts +12 -0
- package/src/modes/utils/keybinding-matchers.ts +4 -0
- package/src/modes/utils/macos-option-forwarding.ts +80 -0
- package/src/progress/collect-project-progress.ts +153 -0
- package/src/progress/progress-contract.ts +172 -0
- package/src/progress/progress-snapshot.ts +140 -0
- package/src/progress/project-progress.ts +446 -0
- package/src/progress/render-progress.ts +209 -0
- package/src/prompts/system/master-mode.md +1 -0
- package/src/prompts/tools/skill-discovery.md +1 -1
- package/src/runtime-mcp/config-writer.ts +10 -6
- package/src/runtime-mcp/config.ts +1 -0
- package/src/runtime-mcp/oauth-flow.ts +5 -0
- package/src/runtime-mcp/oauth-public-url.ts +8 -0
- package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
- package/src/runtime-mcp/smithery-registry.ts +22 -3
- package/src/runtime-mcp/transports/http.ts +2 -2
- package/src/runtime-mcp/types.ts +6 -0
- package/src/sdk/acp/adapter.ts +93 -8
- package/src/sdk/broker/broker-exit.ts +52 -23
- package/src/sdk/broker/broker.ts +75 -6
- package/src/sdk/broker/ensure.ts +2 -0
- package/src/sdk/broker/lifecycle.ts +711 -66
- package/src/sdk/broker/session-index.ts +191 -81
- package/src/sdk/bus/chat-daemon-cli.ts +38 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +238 -43
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -1
- package/src/sdk/cli/session-cli.ts +17 -3
- package/src/sdk/client/client.ts +28 -8
- package/src/sdk/host/query/handlers.ts +9 -1
- package/src/sdk/host/session-runtime.test.ts +2647 -294
- package/src/sdk/host/session-runtime.ts +426 -109
- package/src/sdk/host/surface-policy.ts +1 -0
- package/src/sdk/index.ts +8 -0
- package/src/sdk/lifecycle-session.ts +3 -1
- package/src/sdk/prompt-deadline-manager.ts +15 -0
- package/src/sdk/protocol/operation-inventory.generated.json +146 -0
- package/src/sdk/protocol/operation-registry.ts +9 -2
- package/src/sdk/router/session-router.ts +75 -12
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +67 -38
- package/src/session/agent-session.ts +623 -224
- package/src/session/artifacts.ts +174 -70
- package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
- package/src/session/internal/managed-session-scope.ts +3457 -462
- package/src/session/internal/managed-session-storage.ts +376 -61
- package/src/session/internal/managed-task-owner-cleanup.ts +473 -0
- package/src/session/internal/task-artifact-owner-access.ts +19 -3
- package/src/session/internal/task-artifact-owner-transcript.ts +547 -1
- package/src/session/session-manager.ts +939 -133
- package/src/session/session-retirement.ts +573 -14
- package/src/session/session-storage.ts +417 -14
- package/src/session/task-artifact-owner-codec.ts +71 -8
- package/src/session/task-artifact-owner-retirement.ts +21 -19
- package/src/setup/credential-auto-import.ts +12 -3
- package/src/setup/credential-import.ts +176 -5
- package/src/setup/paseo/announce.ts +6 -1
- package/src/skill-state/active-state.ts +132 -2
- package/src/slash-commands/builtin-registry.ts +39 -9
- package/src/slash-commands/helpers/progress-report.ts +23 -0
- package/src/stt/setup.ts +3 -5
- package/src/task/commands.ts +3 -1
- package/src/task/executor.ts +4 -0
- package/src/task/index.ts +6 -0
- package/src/tools/ask.ts +205 -6
- package/src/tools/ast-edit.ts +4 -0
- package/src/tools/bash-allowed-prefixes.ts +15 -0
- package/src/tools/bash-skill-urls.ts +56 -3
- package/src/tools/bash.ts +48 -79
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/eval.ts +56 -36
- package/src/tools/image-gen.ts +1 -1
- package/src/tools/index.ts +3 -0
- package/src/tools/python.ts +187 -66
- package/src/tools/recipe/index.ts +9 -0
- package/src/tools/recipe/recipe-guard.ts +19 -0
- package/src/tools/restricted-cwd.ts +10 -0
- package/src/tools/skill-discovery.ts +5 -0
- package/src/tools/skill.ts +28 -1
- package/src/tools/todo-write.ts +10 -1
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/tools/write.ts +1 -0
- package/src/utils/tools-checksum.ts +34 -0
- package/src/utils/tools-manager.ts +24 -6
- package/src/web/search/providers/codex.ts +3 -2
|
@@ -39,6 +39,10 @@ const RETIREMENT_CONTINUATION_KEYS = new Set([
|
|
|
39
39
|
const U64_MAX = 18_446_744_073_709_551_615n;
|
|
40
40
|
|
|
41
41
|
type NativeDirectoryTreeEntry = NativeDirectoryTreeSnapshot["entries"][number];
|
|
42
|
+
type ExpectedTreeChildren = {
|
|
43
|
+
readonly direct: Map<string, NativeDirectoryTreeEntry>;
|
|
44
|
+
readonly quarantined: Map<string, NativeDirectoryTreeEntry | null>;
|
|
45
|
+
};
|
|
42
46
|
|
|
43
47
|
export interface TaskArtifactOwnerLocator {
|
|
44
48
|
readonly schemaVersion: typeof OWNER_SCHEMA_VERSION;
|
|
@@ -231,16 +235,66 @@ export function freezeTreeSnapshot(snapshot: NativeDirectoryTreeSnapshot): Nativ
|
|
|
231
235
|
return Object.freeze(canonical);
|
|
232
236
|
}
|
|
233
237
|
|
|
234
|
-
|
|
238
|
+
/** Windows native tree removal hashes original path and inode identity for child quarantine names. */
|
|
239
|
+
function treeQuarantineName(entry: NativeDirectoryTreeEntry): string {
|
|
240
|
+
const material = `${entry.relativePath}\0${entry.dev}\0${entry.ino}`;
|
|
241
|
+
return `.pi-tree-detached-${crypto.createHash("sha256").update(material, "utf8").digest("hex")}`;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Project only authenticated remaining entries; never restore an omitted entry's authority. */
|
|
245
|
+
export function normalizeRetainedTreeSnapshot(
|
|
235
246
|
expected: NativeDirectoryTreeSnapshot,
|
|
236
247
|
retained: NativeDirectoryTreeSnapshot,
|
|
237
|
-
):
|
|
238
|
-
if (expected.rootDev !== retained.rootDev || expected.rootIno !== retained.rootIno) return
|
|
248
|
+
): NativeDirectoryTreeSnapshot | undefined {
|
|
249
|
+
if (expected.rootDev !== retained.rootDev || expected.rootIno !== retained.rootIno) return undefined;
|
|
239
250
|
const expectedEntries = new Map(expected.entries.map(entry => [entry.relativePath, entry]));
|
|
240
|
-
if (retained.entries.length > expected.entries.length) return
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
251
|
+
if (retained.entries.length > expected.entries.length) return undefined;
|
|
252
|
+
|
|
253
|
+
const childrenByParent = new Map<string, ExpectedTreeChildren>();
|
|
254
|
+
for (const entry of expected.entries) {
|
|
255
|
+
if (entry.relativePath === "") continue;
|
|
256
|
+
const separator = entry.relativePath.lastIndexOf("/");
|
|
257
|
+
const parent = separator < 0 ? "" : entry.relativePath.slice(0, separator);
|
|
258
|
+
const name = separator < 0 ? entry.relativePath : entry.relativePath.slice(separator + 1);
|
|
259
|
+
let children = childrenByParent.get(parent);
|
|
260
|
+
if (!children) {
|
|
261
|
+
children = { direct: new Map(), quarantined: new Map() };
|
|
262
|
+
childrenByParent.set(parent, children);
|
|
263
|
+
}
|
|
264
|
+
children.direct.set(name, entry);
|
|
265
|
+
const quarantineName = treeQuarantineName(entry);
|
|
266
|
+
children.quarantined.set(quarantineName, children.quarantined.has(quarantineName) ? null : entry);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const originalPathFor = (retainedPath: string): string | undefined => {
|
|
270
|
+
if (retainedPath === "") return "";
|
|
271
|
+
const components = retainedPath.split("/");
|
|
272
|
+
if (components.some(component => component.length === 0 || component === "." || component === ".."))
|
|
273
|
+
return undefined;
|
|
274
|
+
let parent = "";
|
|
275
|
+
for (const component of components) {
|
|
276
|
+
const children = childrenByParent.get(parent);
|
|
277
|
+
if (!children) return undefined;
|
|
278
|
+
const direct = children.direct.get(component);
|
|
279
|
+
const hasQuarantine = children.quarantined.has(component);
|
|
280
|
+
const quarantined = children.quarantined.get(component);
|
|
281
|
+
if ((direct && hasQuarantine) || quarantined === null) return undefined;
|
|
282
|
+
const matched = direct ?? quarantined;
|
|
283
|
+
if (!matched) return undefined;
|
|
284
|
+
parent = matched.relativePath;
|
|
285
|
+
}
|
|
286
|
+
return parent;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const seenOriginalPaths = new Set<string>();
|
|
290
|
+
const entries: NativeDirectoryTreeEntry[] = [];
|
|
291
|
+
const valid = retained.entries.every(entry => {
|
|
292
|
+
const originalPath = originalPathFor(entry.relativePath);
|
|
293
|
+
if (originalPath === undefined || seenOriginalPaths.has(originalPath)) return false;
|
|
294
|
+
seenOriginalPaths.add(originalPath);
|
|
295
|
+
entries.push({ ...entry, relativePath: originalPath });
|
|
296
|
+
if (originalPath === "") return entry.kind === "directory";
|
|
297
|
+
const authorized = expectedEntries.get(originalPath);
|
|
244
298
|
if (
|
|
245
299
|
authorized === undefined ||
|
|
246
300
|
authorized.kind !== entry.kind ||
|
|
@@ -257,6 +311,14 @@ export function retainedTreeDoesNotExpandAuthority(
|
|
|
257
311
|
entry.sha256 !== undefined || (entry.mtimeNs === authorized.mtimeNs && entry.ctimeNs === authorized.ctimeNs)
|
|
258
312
|
);
|
|
259
313
|
});
|
|
314
|
+
return valid ? { rootDev: retained.rootDev, rootIno: retained.rootIno, entries } : undefined;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function retainedTreeDoesNotExpandAuthority(
|
|
318
|
+
expected: NativeDirectoryTreeSnapshot,
|
|
319
|
+
retained: NativeDirectoryTreeSnapshot,
|
|
320
|
+
): boolean {
|
|
321
|
+
return normalizeRetainedTreeSnapshot(expected, retained) !== undefined;
|
|
260
322
|
}
|
|
261
323
|
|
|
262
324
|
export function sameTreeContents(left: NativeDirectoryTreeSnapshot, right: NativeDirectoryTreeSnapshot): boolean {
|
|
@@ -501,6 +563,7 @@ export function parseTaskArtifactOwnerRetirementOutcome(
|
|
|
501
563
|
"windowsErrorCode",
|
|
502
564
|
];
|
|
503
565
|
if (Object.keys(record).some(key => !allowed.includes(key)) || typeof record.ok !== "boolean") return invalid();
|
|
566
|
+
if (record.ok && Object.keys(record).length !== 1) return invalid();
|
|
504
567
|
for (const [key, paths] of [
|
|
505
568
|
["detachedPath", continuation?.detachedPaths],
|
|
506
569
|
["retainedSuccessorPath", continuation?.retainedSuccessorPaths],
|
|
@@ -547,7 +610,7 @@ export function parseTaskArtifactOwnerRetirementOutcome(
|
|
|
547
610
|
if (!nativeOutcome?.ok) return invalid();
|
|
548
611
|
return Object.freeze({ kind, evidence: original, nativeOutcome });
|
|
549
612
|
}
|
|
550
|
-
if (!continuation || nativeOutcome?.ok) return invalid();
|
|
613
|
+
if (!continuation || (kind !== "uncertain" && nativeOutcome?.ok)) return invalid();
|
|
551
614
|
if (kind === "payload_retired") {
|
|
552
615
|
if (
|
|
553
616
|
value.namespace !== "retained" ||
|
|
@@ -5,6 +5,7 @@ import type { ManagedSessionDescendantStore } from "./internal/managed-session-s
|
|
|
5
5
|
import {
|
|
6
6
|
captureOwnerTreeIfPresent,
|
|
7
7
|
captureValidatedOwnerTree,
|
|
8
|
+
newSessionRootReaderStore,
|
|
8
9
|
newSessionRootStore,
|
|
9
10
|
ownerTreeHasPendingManagedPublication,
|
|
10
11
|
} from "./internal/task-artifact-owner-access";
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
EMPTY_PAYLOAD_SHA256,
|
|
13
14
|
isKnownNativeSidePath,
|
|
14
15
|
isOwnerRetainedRoot,
|
|
16
|
+
normalizeRetainedTreeSnapshot,
|
|
15
17
|
OWNER_DIRECTORY,
|
|
16
18
|
OWNER_RETIREMENT_SCHEMA_VERSION,
|
|
17
19
|
ownerAbsolutePath,
|
|
@@ -181,7 +183,7 @@ export function verifyTaskArtifactOwnerPhysicalRetirement(
|
|
|
181
183
|
): void {
|
|
182
184
|
const outcome = parseTaskArtifactOwnerRetirementOutcome(context, evidence, value);
|
|
183
185
|
if (outcome.kind !== "completed") throw new Error("task_artifact_owner_physical_retirement_unverified");
|
|
184
|
-
const store =
|
|
186
|
+
const store = newSessionRootReaderStore(context);
|
|
185
187
|
try {
|
|
186
188
|
if (!sameOwnerParentIdentity(store.captureDirectoryIdentity(OWNER_DIRECTORY), evidence.parentIdentity))
|
|
187
189
|
throw new Error("task_artifact_owner_parent_identity_mismatch");
|
|
@@ -204,7 +206,7 @@ export function verifyTaskArtifactOwnerRetirementContinuation(
|
|
|
204
206
|
): TaskArtifactOwnerRetirementContinuation {
|
|
205
207
|
const evidence = parseTaskArtifactOwnerDeletionEvidence(evidenceValue);
|
|
206
208
|
const continuation = parseTaskArtifactOwnerRetirementContinuation(context, evidence, value);
|
|
207
|
-
const store =
|
|
209
|
+
const store = newSessionRootReaderStore(context);
|
|
208
210
|
try {
|
|
209
211
|
if (!sameOwnerParentIdentity(store.captureDirectoryIdentity(OWNER_DIRECTORY), evidence.parentIdentity))
|
|
210
212
|
throw new Error("task_artifact_owner_parent_identity_mismatch");
|
|
@@ -214,7 +216,9 @@ export function verifyTaskArtifactOwnerRetirementContinuation(
|
|
|
214
216
|
evidence.locator.ownerId,
|
|
215
217
|
continuation.retainedRootPath,
|
|
216
218
|
);
|
|
217
|
-
|
|
219
|
+
const prior = normalizeRetainedTreeSnapshot(evidence.treeSnapshot, continuation.retainedTreeSnapshot);
|
|
220
|
+
const current = snapshot && normalizeRetainedTreeSnapshot(evidence.treeSnapshot, snapshot);
|
|
221
|
+
if (!prior || !current || !retainedTreeDoesNotExpandAuthority(prior, current))
|
|
218
222
|
throw new Error("task_artifact_owner_retained_tree_mismatch");
|
|
219
223
|
if (
|
|
220
224
|
continuation.payloadDurable === true &&
|
|
@@ -283,7 +287,9 @@ export function retireTaskArtifactOwner(
|
|
|
283
287
|
|
|
284
288
|
const ownerPath = ownerAbsolutePath(context, evidence.locator.ownerId);
|
|
285
289
|
const removingPath = `${ownerPath}.removing`;
|
|
286
|
-
const baseline = previous
|
|
290
|
+
const baseline = previous
|
|
291
|
+
? normalizeRetainedTreeSnapshot(evidence.treeSnapshot, previous.retainedTreeSnapshot)
|
|
292
|
+
: evidence.treeSnapshot;
|
|
287
293
|
const candidates = [
|
|
288
294
|
...new Set(
|
|
289
295
|
[previous?.retainedRootPath, removingPath, ownerPath].filter(
|
|
@@ -293,6 +299,7 @@ export function retireTaskArtifactOwner(
|
|
|
293
299
|
];
|
|
294
300
|
let retainedRootPath: string | undefined;
|
|
295
301
|
let retainedTreeSnapshot: NativeDirectoryTreeSnapshot | undefined;
|
|
302
|
+
let nativeTreeSnapshot: NativeDirectoryTreeSnapshot | undefined;
|
|
296
303
|
for (const candidate of candidates) {
|
|
297
304
|
let snapshot: NativeDirectoryTreeSnapshot | undefined;
|
|
298
305
|
try {
|
|
@@ -312,12 +319,8 @@ export function retireTaskArtifactOwner(
|
|
|
312
319
|
continuation: fallback,
|
|
313
320
|
reason: "task_artifact_owner_writer_not_quiescent",
|
|
314
321
|
};
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
snapshot.rootDev !== evidence.locator.directoryDev ||
|
|
318
|
-
snapshot.rootIno !== evidence.locator.directoryIno ||
|
|
319
|
-
!retainedTreeDoesNotExpandAuthority(baseline, snapshot)
|
|
320
|
-
)
|
|
322
|
+
const normalized = snapshot && normalizeRetainedTreeSnapshot(evidence.treeSnapshot, snapshot);
|
|
323
|
+
if (!snapshot || !normalized || !baseline || !retainedTreeDoesNotExpandAuthority(baseline, normalized))
|
|
321
324
|
continue;
|
|
322
325
|
if (candidate === ownerPath && JSON.stringify(snapshot) === JSON.stringify(evidence.treeSnapshot)) {
|
|
323
326
|
try {
|
|
@@ -340,9 +343,10 @@ export function retireTaskArtifactOwner(
|
|
|
340
343
|
}
|
|
341
344
|
retainedRootPath = candidate;
|
|
342
345
|
retainedTreeSnapshot = snapshot;
|
|
346
|
+
nativeTreeSnapshot = normalized;
|
|
343
347
|
break;
|
|
344
348
|
}
|
|
345
|
-
if (!retainedRootPath || !retainedTreeSnapshot)
|
|
349
|
+
if (!retainedRootPath || !retainedTreeSnapshot || !nativeTreeSnapshot)
|
|
346
350
|
return {
|
|
347
351
|
kind: "uncertain",
|
|
348
352
|
evidence,
|
|
@@ -354,7 +358,7 @@ export function retireTaskArtifactOwner(
|
|
|
354
358
|
try {
|
|
355
359
|
nativeOutcome = rootStore.removeTreeExpectedWithParentIdentity(
|
|
356
360
|
path.relative(context.sessionsRoot, retainedRootPath).split(path.sep).join("/"),
|
|
357
|
-
|
|
361
|
+
nativeTreeSnapshot,
|
|
358
362
|
{ dev: BigInt(evidence.parentIdentity.dev), ino: BigInt(evidence.parentIdentity.ino) },
|
|
359
363
|
);
|
|
360
364
|
} catch {
|
|
@@ -391,13 +395,11 @@ export function retireTaskArtifactOwner(
|
|
|
391
395
|
let residualValid = reportedRootKnown;
|
|
392
396
|
try {
|
|
393
397
|
residualTree = captureOwnerTreeIfPresent(context, rootStore, evidence.locator.ownerId, nextRoot);
|
|
394
|
-
if (
|
|
395
|
-
residualTree
|
|
396
|
-
(
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
)
|
|
400
|
-
residualValid = false;
|
|
398
|
+
if (residualTree) {
|
|
399
|
+
const normalizedResidual = normalizeRetainedTreeSnapshot(evidence.treeSnapshot, residualTree);
|
|
400
|
+
if (!normalizedResidual || !retainedTreeDoesNotExpandAuthority(nativeTreeSnapshot, normalizedResidual))
|
|
401
|
+
residualValid = false;
|
|
402
|
+
}
|
|
401
403
|
} catch {
|
|
402
404
|
residualValid = false;
|
|
403
405
|
}
|
|
@@ -36,7 +36,11 @@ export const CREDENTIAL_AUTO_IMPORT_RETRY_WARNING =
|
|
|
36
36
|
export const CREDENTIAL_AUTO_IMPORT_STATE_UNREADABLE_WARNING =
|
|
37
37
|
"Credential import preference could not be read. External credential discovery was skipped; use /provider to import credentials manually.";
|
|
38
38
|
|
|
39
|
-
export type CredentialAutoImportSourceLabel =
|
|
39
|
+
export type CredentialAutoImportSourceLabel =
|
|
40
|
+
| "claude-code-file"
|
|
41
|
+
| "claude-code-keychain"
|
|
42
|
+
| "codex-file"
|
|
43
|
+
| "kiro-cli-social";
|
|
40
44
|
export type CredentialAutoImportTrigger = "startup" | "bare-login" | "setup-cli";
|
|
41
45
|
export type InitialImportResolution = "accepted" | "declined";
|
|
42
46
|
export type CredentialAutoImportStateProblem =
|
|
@@ -259,16 +263,18 @@ export interface CredentialAutoImportResult {
|
|
|
259
263
|
globalDiscoveryFailure?: CredentialAutoImportFailure;
|
|
260
264
|
}
|
|
261
265
|
|
|
262
|
-
const CREDENTIAL_AUTO_IMPORT_PROMPT_PROVIDERS: readonly ExternalProvider[] = ["anthropic", "openai-codex"];
|
|
266
|
+
const CREDENTIAL_AUTO_IMPORT_PROMPT_PROVIDERS: readonly ExternalProvider[] = ["anthropic", "openai-codex", "kiro"];
|
|
263
267
|
const CREDENTIAL_AUTO_IMPORT_PROMPT_ORIGINS: readonly CredentialOrigin[] = [
|
|
264
268
|
"claude-code-file",
|
|
265
269
|
"claude-code-keychain",
|
|
266
270
|
"codex-file",
|
|
271
|
+
"kiro-cli-social",
|
|
267
272
|
];
|
|
268
273
|
const CREDENTIAL_AUTO_IMPORT_ORIGIN_LABELS: Record<CredentialOrigin, string> = {
|
|
269
274
|
"claude-code-file": "Claude Code file",
|
|
270
275
|
"claude-code-keychain": "Claude Code Keychain",
|
|
271
276
|
"codex-file": "Codex CLI file",
|
|
277
|
+
"kiro-cli-social": "Kiro CLI social login",
|
|
272
278
|
};
|
|
273
279
|
const CREDENTIAL_AUTO_IMPORT_FAILURE_CLASSES = new Set(Object.values(CredentialAutoImportFailureClass));
|
|
274
280
|
|
|
@@ -413,7 +419,10 @@ export function buildCredentialAutoImportNotice(
|
|
|
413
419
|
...new Set(result.imported.map(c => EXTERNAL_PROVIDER_LABELS[c.provider as ExternalProvider] ?? c.provider)),
|
|
414
420
|
];
|
|
415
421
|
const success = `Imported ${result.imported.length} external OAuth credential(s) into gjc: ${providers.join(", ")}.`;
|
|
416
|
-
|
|
422
|
+
const mayRotateExternalRefreshToken = result.imported.some(
|
|
423
|
+
credential => credential.provider === "anthropic" || credential.provider === "openai-codex",
|
|
424
|
+
);
|
|
425
|
+
return mayRotateExternalRefreshToken ? `${success}\n${CREDENTIAL_AUTO_IMPORT_ROTATION_WARNING}` : success;
|
|
417
426
|
}
|
|
418
427
|
|
|
419
428
|
export function formatCredentialAutoImportResult(result: CredentialAutoImportResult): string[] {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Discover and import existing Claude Code
|
|
2
|
+
* Discover and import existing Claude Code, Codex CLI, and Kiro CLI credentials.
|
|
3
3
|
*
|
|
4
4
|
* This is the testable core behind `gjc setup credentials` (CLI, primary entry)
|
|
5
5
|
* and the TUI provider-onboarding "import existing credentials" action. It never
|
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
* Keychain (`Claude Code-credentials`), and env vars.
|
|
13
13
|
* - Codex CLI: `$CODEX_HOME/auth.json` (defaults to `~/.codex/auth.json`;
|
|
14
14
|
* OAuth `tokens` block or stored `OPENAI_API_KEY`), and env vars.
|
|
15
|
+
* - Kiro CLI social OAuth: `data.sqlite3` in the platform's app-data directory,
|
|
16
|
+
* read-only from `auth_kv` key `kirocli:social:token`.
|
|
15
17
|
*/
|
|
18
|
+
import { Database } from "bun:sqlite";
|
|
16
19
|
import * as fs from "node:fs/promises";
|
|
17
20
|
import * as os from "node:os";
|
|
18
21
|
import * as path from "node:path";
|
|
@@ -21,20 +24,22 @@ import { $credentialEnv, isEnoent } from "@gajae-code/utils";
|
|
|
21
24
|
import { redactSecret } from "./provider-onboarding";
|
|
22
25
|
|
|
23
26
|
/** gjc provider ids that external credentials map onto. */
|
|
24
|
-
export type ExternalProvider = "anthropic" | "openai-codex";
|
|
27
|
+
export type ExternalProvider = "anthropic" | "openai-codex" | "kiro";
|
|
25
28
|
|
|
26
29
|
/** Where a discovered credential came from. */
|
|
27
|
-
export type CredentialOrigin = "claude-code-file" | "claude-code-keychain" | "codex-file";
|
|
30
|
+
export type CredentialOrigin = "claude-code-file" | "claude-code-keychain" | "codex-file" | "kiro-cli-social";
|
|
28
31
|
|
|
29
32
|
export const AUTO_IMPORT_OAUTH_PROVIDER_ORIGINS: Record<ExternalProvider, ReadonlySet<CredentialOrigin>> = {
|
|
30
33
|
anthropic: new Set<CredentialOrigin>(["claude-code-file", "claude-code-keychain"]),
|
|
31
34
|
"openai-codex": new Set<CredentialOrigin>(["codex-file"]),
|
|
35
|
+
kiro: new Set<CredentialOrigin>(["kiro-cli-social"]),
|
|
32
36
|
};
|
|
33
37
|
|
|
34
38
|
/** Human labels for providers, used in redacted summaries. */
|
|
35
39
|
export const EXTERNAL_PROVIDER_LABELS: Record<ExternalProvider, string> = {
|
|
36
40
|
anthropic: "Claude (Anthropic)",
|
|
37
41
|
"openai-codex": "Codex (ChatGPT)",
|
|
42
|
+
kiro: "Kiro",
|
|
38
43
|
};
|
|
39
44
|
|
|
40
45
|
/** A credential that can be safely imported into gjc's store. */
|
|
@@ -98,6 +103,8 @@ export interface DiscoveryOptions {
|
|
|
98
103
|
* or `null` when no entry exists.
|
|
99
104
|
*/
|
|
100
105
|
readClaudeKeychain?: () => Promise<string | null>;
|
|
106
|
+
/** Absolute Kiro CLI data.sqlite3 override, primarily for tests and embedders. */
|
|
107
|
+
kiroCliDatabasePath?: string;
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
export type CredentialUpserter = (provider: string, credential: AuthCredential) => unknown | Promise<unknown>;
|
|
@@ -131,6 +138,7 @@ interface CodexAuthFile {
|
|
|
131
138
|
|
|
132
139
|
const ANTHROPIC_ENV_KEYS = ["ANTHROPIC_API_KEY", "CLAUDE_CODE_OAUTH_TOKEN"] as const;
|
|
133
140
|
const OPENAI_ENV_KEYS = ["OPENAI_API_KEY"] as const;
|
|
141
|
+
const KIRO_CLI_SOCIAL_TOKEN_KEY = "kirocli:social:token";
|
|
134
142
|
|
|
135
143
|
// ─── JWT helpers (best-effort identity/expiry extraction) ────────────────────
|
|
136
144
|
|
|
@@ -368,6 +376,154 @@ async function discoverCodex(home: ResolvedExternalDir, result: CredentialDiscov
|
|
|
368
376
|
pushOutcome(result, parseCodexAuth(raw, `Codex CLI (${displayPath})`));
|
|
369
377
|
}
|
|
370
378
|
|
|
379
|
+
interface KiroCliSocialToken {
|
|
380
|
+
access_token?: unknown;
|
|
381
|
+
refresh_token?: unknown;
|
|
382
|
+
expires_at?: unknown;
|
|
383
|
+
profile_arn?: unknown;
|
|
384
|
+
provider?: unknown;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function parseKiroCliExpiry(value: unknown): number | undefined {
|
|
388
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
389
|
+
if (value > 1_000_000_000_000) return value;
|
|
390
|
+
if (value > 1_000_000_000) return value * 1000;
|
|
391
|
+
return undefined;
|
|
392
|
+
}
|
|
393
|
+
if (typeof value !== "string" || value.trim().length === 0) return undefined;
|
|
394
|
+
const numericValue = Number(value);
|
|
395
|
+
if (Number.isFinite(numericValue)) return parseKiroCliExpiry(numericValue);
|
|
396
|
+
const parsed = Date.parse(value);
|
|
397
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function parseKiroCliSocialToken(raw: string, source: string): ImportableCredential | SkippedCredential {
|
|
401
|
+
let parsed: unknown;
|
|
402
|
+
try {
|
|
403
|
+
parsed = JSON.parse(raw) as KiroCliSocialToken;
|
|
404
|
+
} catch (err) {
|
|
405
|
+
return { origin: "kiro-cli-social", source, reason: sanitizedFailureReason("malformed credential file", err) };
|
|
406
|
+
}
|
|
407
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
408
|
+
return { origin: "kiro-cli-social", source, reason: "unsupported token shape (root is not an object)" };
|
|
409
|
+
}
|
|
410
|
+
const token = parsed as KiroCliSocialToken;
|
|
411
|
+
const access = nonEmptyString(token.access_token);
|
|
412
|
+
const refresh = nonEmptyString(token.refresh_token);
|
|
413
|
+
const profileArn = nonEmptyString(token.profile_arn);
|
|
414
|
+
const provider = nonEmptyString(token.provider);
|
|
415
|
+
if (!access || !refresh || !profileArn || !provider) {
|
|
416
|
+
return { origin: "kiro-cli-social", source, reason: "incomplete Kiro social OAuth token metadata" };
|
|
417
|
+
}
|
|
418
|
+
const expiresAt = parseKiroCliExpiry(token.expires_at);
|
|
419
|
+
const credential: OAuthCredential = {
|
|
420
|
+
type: "oauth",
|
|
421
|
+
access,
|
|
422
|
+
refresh,
|
|
423
|
+
expires: expiresAt ?? Date.now(),
|
|
424
|
+
profileArn,
|
|
425
|
+
};
|
|
426
|
+
return {
|
|
427
|
+
provider: "kiro",
|
|
428
|
+
origin: "kiro-cli-social",
|
|
429
|
+
source,
|
|
430
|
+
kind: "oauth",
|
|
431
|
+
...(expiresAt === undefined ? {} : { expiresAt }),
|
|
432
|
+
redactedToken: redactSecret(access),
|
|
433
|
+
credential,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function resolveKiroCliDatabasePath(
|
|
438
|
+
homeDir: string,
|
|
439
|
+
platform: NodeJS.Platform,
|
|
440
|
+
env: Record<string, string | undefined>,
|
|
441
|
+
): string {
|
|
442
|
+
if (platform === "darwin") {
|
|
443
|
+
return path.join(homeDir, "Library", "Application Support", "kiro-cli", "data.sqlite3");
|
|
444
|
+
}
|
|
445
|
+
if (platform === "win32") {
|
|
446
|
+
const localAppData = nonEmptyString(env.LOCALAPPDATA);
|
|
447
|
+
const dataDir =
|
|
448
|
+
localAppData && path.isAbsolute(localAppData) ? localAppData : path.join(homeDir, "AppData", "Local");
|
|
449
|
+
return path.join(dataDir, "kiro-cli", "data.sqlite3");
|
|
450
|
+
}
|
|
451
|
+
const xdgDataHome = nonEmptyString(env.XDG_DATA_HOME);
|
|
452
|
+
const dataDir = xdgDataHome && path.isAbsolute(xdgDataHome) ? xdgDataHome : path.join(homeDir, ".local", "share");
|
|
453
|
+
return path.join(dataDir, "kiro-cli", "data.sqlite3");
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async function discoverKiroCliSocialCredential(
|
|
457
|
+
homeDir: string,
|
|
458
|
+
platform: NodeJS.Platform,
|
|
459
|
+
pathEnv: Record<string, string | undefined>,
|
|
460
|
+
databasePathOverride: string | undefined,
|
|
461
|
+
result: CredentialDiscoveryResult,
|
|
462
|
+
): Promise<void> {
|
|
463
|
+
const source = "Kiro CLI (social login)";
|
|
464
|
+
if (databasePathOverride && !path.isAbsolute(databasePathOverride)) {
|
|
465
|
+
result.skipped.push({ origin: "kiro-cli-social", source, reason: "Kiro CLI database path must be absolute" });
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const databasePath = databasePathOverride ?? resolveKiroCliDatabasePath(homeDir, platform, pathEnv);
|
|
469
|
+
let database: Database | undefined;
|
|
470
|
+
try {
|
|
471
|
+
const stat = await fs.stat(databasePath);
|
|
472
|
+
if (!stat.isFile()) {
|
|
473
|
+
result.skipped.push({ origin: "kiro-cli-social", source, reason: "Kiro CLI database is not a regular file" });
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
} catch (err) {
|
|
477
|
+
if (!isEnoent(err)) {
|
|
478
|
+
result.skipped.push({
|
|
479
|
+
origin: "kiro-cli-social",
|
|
480
|
+
source,
|
|
481
|
+
reason: sanitizedFailureReason("unreadable credential file", err),
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
try {
|
|
488
|
+
database = new Database(databasePath, { readonly: true, create: false, strict: true });
|
|
489
|
+
database.run("PRAGMA busy_timeout = 1000");
|
|
490
|
+
const authTable = database
|
|
491
|
+
.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?")
|
|
492
|
+
.get("auth_kv");
|
|
493
|
+
if (!authTable) {
|
|
494
|
+
result.skipped.push({ origin: "kiro-cli-social", source, reason: "Kiro CLI database is missing auth_kv" });
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const row = database.prepare("SELECT value FROM auth_kv WHERE key = ?").get(KIRO_CLI_SOCIAL_TOKEN_KEY) as {
|
|
498
|
+
value?: unknown;
|
|
499
|
+
} | null;
|
|
500
|
+
if (!row) return;
|
|
501
|
+
const value =
|
|
502
|
+
typeof row.value === "string"
|
|
503
|
+
? row.value
|
|
504
|
+
: row.value instanceof Uint8Array
|
|
505
|
+
? new TextDecoder().decode(row.value)
|
|
506
|
+
: undefined;
|
|
507
|
+
if (value === undefined) {
|
|
508
|
+
result.skipped.push({
|
|
509
|
+
origin: "kiro-cli-social",
|
|
510
|
+
source,
|
|
511
|
+
reason: "Kiro CLI token value has an unsupported type",
|
|
512
|
+
});
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
pushOutcome(result, parseKiroCliSocialToken(value, source));
|
|
516
|
+
} catch (err) {
|
|
517
|
+
result.skipped.push({
|
|
518
|
+
origin: "kiro-cli-social",
|
|
519
|
+
source,
|
|
520
|
+
reason: sanitizedFailureReason("unreadable credential file", err),
|
|
521
|
+
});
|
|
522
|
+
} finally {
|
|
523
|
+
database?.close();
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
371
527
|
// ─── Environment hints ───────────────────────────────────────────────────────
|
|
372
528
|
|
|
373
529
|
function discoverEnvironment(env: Record<string, string | undefined>, result: CredentialDiscoveryResult): void {
|
|
@@ -421,8 +577,8 @@ function resolveExternalDir(
|
|
|
421
577
|
}
|
|
422
578
|
|
|
423
579
|
/**
|
|
424
|
-
* Discover Claude Code and
|
|
425
|
-
*
|
|
580
|
+
* Discover Claude Code, Codex CLI, and Kiro CLI credentials across their local
|
|
581
|
+
* stores and environment variables. Never throws for individual unreadable or
|
|
426
582
|
* malformed sources — those land in {@link CredentialDiscoveryResult.skipped}.
|
|
427
583
|
*/
|
|
428
584
|
export async function discoverExternalCredentials(options: DiscoveryOptions = {}): Promise<CredentialDiscoveryResult> {
|
|
@@ -436,11 +592,16 @@ export async function discoverExternalCredentials(options: DiscoveryOptions = {}
|
|
|
436
592
|
? { dir: options.codexHome, display: "$CODEX_HOME" }
|
|
437
593
|
: resolveExternalDir("CODEX_HOME", options.env, path.join(homeDir, ".codex"), "~/.codex");
|
|
438
594
|
const result: CredentialDiscoveryResult = { importable: [], skipped: [], environment: [] };
|
|
595
|
+
const kiroPathEnv = options.env ?? {
|
|
596
|
+
LOCALAPPDATA: $credentialEnv("LOCALAPPDATA"),
|
|
597
|
+
XDG_DATA_HOME: $credentialEnv("XDG_DATA_HOME"),
|
|
598
|
+
};
|
|
439
599
|
await discoverClaudeCode(
|
|
440
600
|
{ configDir: claudeConfigDir, platform, readClaudeKeychain: options.readClaudeKeychain },
|
|
441
601
|
result,
|
|
442
602
|
);
|
|
443
603
|
await discoverCodex(codexHome, result);
|
|
604
|
+
await discoverKiroCliSocialCredential(homeDir, platform, kiroPathEnv, options.kiroCliDatabasePath, result);
|
|
444
605
|
discoverEnvironment(env, result);
|
|
445
606
|
return result;
|
|
446
607
|
}
|
|
@@ -487,6 +648,16 @@ function hasAutoImportOAuthProviderOrigin(credential: ImportableCredential): boo
|
|
|
487
648
|
}
|
|
488
649
|
|
|
489
650
|
export function isAutoImportOAuthCredential(credential: ImportableCredential, now: number = Date.now()): boolean {
|
|
651
|
+
const refreshableKiroSocialCredential =
|
|
652
|
+
credential.provider === "kiro" &&
|
|
653
|
+
credential.origin === "kiro-cli-social" &&
|
|
654
|
+
credential.kind === "oauth" &&
|
|
655
|
+
credential.credential.type === "oauth" &&
|
|
656
|
+
credential.credential.access.length > 0 &&
|
|
657
|
+
credential.credential.refresh.length > 0 &&
|
|
658
|
+
typeof credential.credential.profileArn === "string" &&
|
|
659
|
+
credential.credential.profileArn.length > 0;
|
|
660
|
+
if (refreshableKiroSocialCredential) return true;
|
|
490
661
|
return (
|
|
491
662
|
hasAutoImportOAuthProviderOrigin(credential) &&
|
|
492
663
|
typeof credential.expiresAt === "number" &&
|
|
@@ -152,7 +152,12 @@ export function parseDaemonListen(raw: string): PaseoDaemonTarget | undefined {
|
|
|
152
152
|
const withoutQuery = authority.split("?")[0] ?? "";
|
|
153
153
|
const separator = withoutQuery.lastIndexOf(":");
|
|
154
154
|
if (separator <= 0) return undefined;
|
|
155
|
-
|
|
155
|
+
// Digits only, like the bare-port branch above: `Number()` also accepts hex
|
|
156
|
+
// (`0x1A0B`), exponents (`1e3`), decimals and padding, which would probe a
|
|
157
|
+
// different port than the raw spelling handed to `paseo import`.
|
|
158
|
+
const portText = withoutQuery.slice(separator + 1);
|
|
159
|
+
if (!/^\d+$/.test(portText)) return undefined;
|
|
160
|
+
const port = Number(portText);
|
|
156
161
|
if (!Number.isSafeInteger(port) || port < 1 || port > 65_535) return undefined;
|
|
157
162
|
const host = withoutQuery.slice(0, separator).replace(/^\[|]$/g, "");
|
|
158
163
|
return host ? { kind: "tcp", host, port } : undefined;
|