@gajae-code/coding-agent 0.18.6 → 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 +318 -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 +6 -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/settings.d.ts +4 -0
- 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 +8 -3
- 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/elicitation.d.ts +12 -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/lifecycle.d.ts +9 -0
- package/dist/types/sdk/broker/session-index.d.ts +12 -6
- package/dist/types/sdk/broker/task-artifact-owner-validation.d.ts +55 -0
- 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/bus/telegram-daemon.d.ts +3 -2
- package/dist/types/sdk/client/client.d.ts +2 -0
- package/dist/types/sdk/host/host.d.ts +6 -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 +9 -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 +36 -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 +19 -0
- package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +11 -0
- package/dist/types/session/session-manager.d.ts +31 -5
- package/dist/types/session/session-retirement.d.ts +71 -0
- package/dist/types/session/session-storage.d.ts +62 -48
- package/dist/types/session/session-transcript-header.d.ts +2 -0
- package/dist/types/session/task-artifact-owner-codec.d.ts +109 -0
- package/dist/types/session/task-artifact-owner-retirement.d.ts +7 -0
- package/dist/types/session/task-artifact-owner.d.ts +10 -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/resident-memory-bench.ts +174 -20
- package/scripts/verify-managed-empty-stop.ts +28 -0
- package/src/async/job-manager.ts +228 -122
- package/src/capability/index.ts +18 -0
- package/src/capability/mcp.ts +2 -0
- package/src/capability/skill.ts +2 -0
- package/src/capability/types.ts +6 -0
- package/src/cli/auth-broker-cli.ts +1 -1
- package/src/cli/config-cli.ts +8 -18
- 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/cli-main.ts +7 -8
- package/src/commands/deep-interview.ts +47 -8
- package/src/commands/sdk.ts +140 -29
- 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/settings.ts +44 -4
- 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 +168 -23
- package/src/discovery/mcp-json.ts +6 -0
- package/src/discovery/ssh.ts +9 -4
- 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 -8
- 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/hindsight/config.ts +9 -3
- package/src/internal-urls/docs-index.generated.ts +19 -18
- 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 +163 -7
- 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/client.ts +13 -4
- package/src/runtime-mcp/config-writer.ts +10 -6
- package/src/runtime-mcp/config.ts +1 -0
- package/src/runtime-mcp/elicitation.ts +142 -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 +83 -7
- package/src/sdk/broker/ensure.ts +2 -0
- package/src/sdk/broker/lifecycle.ts +820 -73
- package/src/sdk/broker/session-index.ts +191 -81
- package/src/sdk/broker/task-artifact-owner-validation.ts +259 -0
- package/src/sdk/bus/chat-daemon-cli.ts +38 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +3 -0
- package/src/sdk/bus/index.ts +241 -46
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -1
- package/src/sdk/bus/telegram-daemon.ts +4 -2
- package/src/sdk/cli/session-cli.ts +17 -3
- package/src/sdk/client/client.ts +28 -8
- package/src/sdk/host/host.ts +6 -0
- package/src/sdk/host/query/handlers.ts +9 -1
- package/src/sdk/host/session-runtime.test.ts +2805 -411
- package/src/sdk/host/session-runtime.ts +428 -111
- 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/mcp/server.ts +15 -3
- 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 +94 -14
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +81 -38
- package/src/session/agent-session.ts +674 -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 +482 -61
- package/src/session/internal/managed-task-owner-cleanup.ts +473 -0
- package/src/session/internal/task-artifact-owner-access.ts +245 -0
- package/src/session/internal/task-artifact-owner-transcript.ts +614 -0
- package/src/session/session-manager.ts +939 -133
- package/src/session/session-retirement.ts +714 -0
- package/src/session/session-storage.ts +418 -174
- package/src/session/session-transcript-header.ts +17 -0
- package/src/session/task-artifact-owner-codec.ts +641 -0
- package/src/session/task-artifact-owner-retirement.ts +477 -0
- package/src/session/task-artifact-owner.ts +289 -0
- 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
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { ArtifactManager } from "./artifacts";
|
|
5
|
+
import { acquireManagedLock, type ManagedSessionDescendantStore } from "./internal/managed-session-storage";
|
|
6
|
+
import {
|
|
7
|
+
locatorFromManifest,
|
|
8
|
+
newSessionRootStore,
|
|
9
|
+
type OwnerManifest,
|
|
10
|
+
openOwnerStore,
|
|
11
|
+
readOwnerManifest,
|
|
12
|
+
} from "./internal/task-artifact-owner-access";
|
|
13
|
+
import type { NativeDirectoryTreeSnapshot } from "./session-storage";
|
|
14
|
+
import {
|
|
15
|
+
assertSafeRelativePath,
|
|
16
|
+
assertSessionRoot,
|
|
17
|
+
OWNER_DIRECTORY,
|
|
18
|
+
OWNER_LOCK_DIRECTORY,
|
|
19
|
+
OWNER_MANIFEST,
|
|
20
|
+
OWNER_SCHEMA_VERSION,
|
|
21
|
+
ownerIdForSession,
|
|
22
|
+
ownerRelativePath,
|
|
23
|
+
parseTaskArtifactOwnerLocator,
|
|
24
|
+
sameTreeContents,
|
|
25
|
+
type TaskArtifactOwnerLocator,
|
|
26
|
+
type TaskArtifactOwnerStorageContext,
|
|
27
|
+
} from "./task-artifact-owner-codec";
|
|
28
|
+
|
|
29
|
+
export interface ManagedTaskArtifactOwner {
|
|
30
|
+
readonly locator: TaskArtifactOwnerLocator;
|
|
31
|
+
readonly manager: ArtifactManager;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function makeOwnerManager(ownerStore: ManagedSessionDescendantStore): ArtifactManager {
|
|
35
|
+
return new ArtifactManager(ownerStore);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function legacyPathOverlapsOwner(
|
|
39
|
+
sessionsRoot: string,
|
|
40
|
+
sourceRelativePath: string,
|
|
41
|
+
destinationRelativePath: string,
|
|
42
|
+
): boolean {
|
|
43
|
+
const source = path.resolve(sessionsRoot, sourceRelativePath);
|
|
44
|
+
const destination = path.resolve(sessionsRoot, destinationRelativePath);
|
|
45
|
+
const sourceToDestination = path.relative(source, destination);
|
|
46
|
+
const destinationToSource = path.relative(destination, source);
|
|
47
|
+
return (
|
|
48
|
+
source !== destination &&
|
|
49
|
+
((!path.isAbsolute(sourceToDestination) &&
|
|
50
|
+
sourceToDestination !== ".." &&
|
|
51
|
+
!sourceToDestination.startsWith(`..${path.sep}`)) ||
|
|
52
|
+
(!path.isAbsolute(destinationToSource) &&
|
|
53
|
+
destinationToSource !== ".." &&
|
|
54
|
+
!destinationToSource.startsWith(`..${path.sep}`)))
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function copyLegacyArtifactTree(
|
|
59
|
+
store: ManagedSessionDescendantStore,
|
|
60
|
+
sourceRelativePath: string,
|
|
61
|
+
destinationRelativePath: string,
|
|
62
|
+
sourceSnapshot: NativeDirectoryTreeSnapshot,
|
|
63
|
+
): Promise<void> {
|
|
64
|
+
for (const entry of sourceSnapshot.entries) {
|
|
65
|
+
if (entry.relativePath === "") continue;
|
|
66
|
+
const destination = path.posix.join(destinationRelativePath, entry.relativePath);
|
|
67
|
+
if (entry.kind === "directory") {
|
|
68
|
+
store.ensureDirectory(destination);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const sourceName = path.posix.join(sourceRelativePath, entry.relativePath);
|
|
72
|
+
const captured = store.readExpected(sourceName);
|
|
73
|
+
if (
|
|
74
|
+
!captured ||
|
|
75
|
+
captured.identity.dev.toString() !== entry.dev ||
|
|
76
|
+
captured.identity.ino.toString() !== entry.ino ||
|
|
77
|
+
String(captured.identity.size) !== entry.size ||
|
|
78
|
+
captured.identity.mtimeNs.toString() !== entry.mtimeNs ||
|
|
79
|
+
captured.identity.ctimeNs.toString() !== entry.ctimeNs ||
|
|
80
|
+
crypto.createHash("sha256").update(captured.bytes).digest("hex") !== entry.sha256
|
|
81
|
+
)
|
|
82
|
+
throw new Error("task_artifact_owner_source_changed");
|
|
83
|
+
await store.publishNoReplace(destination, captured.bytes);
|
|
84
|
+
}
|
|
85
|
+
const sourceAfter = store.captureTree(sourceRelativePath);
|
|
86
|
+
const destinationAfter = store.captureTree(destinationRelativePath);
|
|
87
|
+
if (
|
|
88
|
+
JSON.stringify(sourceAfter) !== JSON.stringify(sourceSnapshot) ||
|
|
89
|
+
!sameTreeContents(sourceSnapshot, destinationAfter)
|
|
90
|
+
)
|
|
91
|
+
throw new Error("task_artifact_owner_copy_mismatch");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function taskArtifactOwnerFromStore(
|
|
95
|
+
locator: TaskArtifactOwnerLocator,
|
|
96
|
+
ownerStore: ManagedSessionDescendantStore,
|
|
97
|
+
): ManagedTaskArtifactOwner {
|
|
98
|
+
return Object.freeze({ locator: Object.freeze({ ...locator }), manager: makeOwnerManager(ownerStore) });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
interface FailureState {
|
|
102
|
+
failed: boolean;
|
|
103
|
+
value: unknown;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function recordFailure(state: FailureState, error: unknown, message: string): void {
|
|
107
|
+
state.value = state.failed ? new AggregateError([state.value, error], message) : error;
|
|
108
|
+
state.failed = true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function closeManagedStore(
|
|
112
|
+
store: ManagedSessionDescendantStore | undefined,
|
|
113
|
+
state: FailureState,
|
|
114
|
+
message: string,
|
|
115
|
+
): void {
|
|
116
|
+
if (!store) return;
|
|
117
|
+
try {
|
|
118
|
+
store.close();
|
|
119
|
+
} catch (error) {
|
|
120
|
+
recordFailure(state, error, message);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function closeReturnedOwner(result: ManagedTaskArtifactOwner | undefined, state: FailureState): void {
|
|
125
|
+
closeManagedStore(result?.manager.getManagedStore(), state, "Closing the task artifact owner also failed.");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Reopen an owner only beneath the captured managed sessions-root authority. */
|
|
129
|
+
export function restoreManagedTaskArtifactOwner(
|
|
130
|
+
context: TaskArtifactOwnerStorageContext,
|
|
131
|
+
sessionId: string,
|
|
132
|
+
locatorValue: unknown,
|
|
133
|
+
): ManagedTaskArtifactOwner {
|
|
134
|
+
const locator = parseTaskArtifactOwnerLocator(locatorValue);
|
|
135
|
+
if (!locator) throw new Error("task_artifact_owner_locator_missing");
|
|
136
|
+
const rootStore = newSessionRootStore(context);
|
|
137
|
+
const state: FailureState = { failed: false, value: undefined };
|
|
138
|
+
let result: ManagedTaskArtifactOwner | undefined;
|
|
139
|
+
try {
|
|
140
|
+
const ownerStore = openOwnerStore(context, rootStore, locator, sessionId);
|
|
141
|
+
result = taskArtifactOwnerFromStore(locator, ownerStore);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
recordFailure(state, error, "Restoring the task artifact owner failed.");
|
|
144
|
+
}
|
|
145
|
+
closeManagedStore(rootStore, state, "Restoring the task artifact owner and closing its root store failed.");
|
|
146
|
+
if (state.failed) {
|
|
147
|
+
closeReturnedOwner(result, state);
|
|
148
|
+
throw state.value;
|
|
149
|
+
}
|
|
150
|
+
if (!result) throw new Error("task_artifact_owner_unavailable");
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Establish or restore the logical-session owner under the captured managed sessions authority. */
|
|
155
|
+
export async function ensureManagedTaskArtifactOwner(
|
|
156
|
+
context: TaskArtifactOwnerStorageContext,
|
|
157
|
+
sessionId: string,
|
|
158
|
+
locatorValue: unknown,
|
|
159
|
+
legacyArtifactRelativePath?: string,
|
|
160
|
+
): Promise<ManagedTaskArtifactOwner> {
|
|
161
|
+
assertSessionRoot(context);
|
|
162
|
+
const locator = parseTaskArtifactOwnerLocator(locatorValue);
|
|
163
|
+
const ownerId = locator?.ownerId ?? ownerIdForSession(sessionId);
|
|
164
|
+
if (ownerId !== ownerIdForSession(sessionId)) throw new Error("task_artifact_owner_session_mismatch");
|
|
165
|
+
const lock = await acquireManagedLock(
|
|
166
|
+
path.join(context.sessionsRoot, OWNER_LOCK_DIRECTORY),
|
|
167
|
+
`owner-${ownerId}`,
|
|
168
|
+
context.rootAuthority,
|
|
169
|
+
context.securityPolicy,
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
let result: ManagedTaskArtifactOwner | undefined;
|
|
173
|
+
const state: FailureState = { failed: false, value: undefined };
|
|
174
|
+
try {
|
|
175
|
+
lock.assertOwned();
|
|
176
|
+
if (locator) {
|
|
177
|
+
result = restoreManagedTaskArtifactOwner(context, sessionId, locator);
|
|
178
|
+
} else {
|
|
179
|
+
const rootStore = newSessionRootStore(context);
|
|
180
|
+
let ownerStore: ManagedSessionDescendantStore | undefined;
|
|
181
|
+
try {
|
|
182
|
+
rootStore.verifyRootSecurity();
|
|
183
|
+
const ownerRelative = ownerRelativePath(ownerId);
|
|
184
|
+
const ownerPath = path.join(context.sessionsRoot, ownerRelative);
|
|
185
|
+
let existingDirectory = false;
|
|
186
|
+
try {
|
|
187
|
+
const stat = fs.lstatSync(ownerPath, { bigint: true });
|
|
188
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error("task_artifact_owner_replaced");
|
|
189
|
+
existingDirectory = true;
|
|
190
|
+
} catch (error) {
|
|
191
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (existingDirectory) {
|
|
195
|
+
const identity = rootStore.captureDirectoryIdentity(ownerRelative);
|
|
196
|
+
const existingLocator: TaskArtifactOwnerLocator = {
|
|
197
|
+
schemaVersion: OWNER_SCHEMA_VERSION,
|
|
198
|
+
ownerId,
|
|
199
|
+
directoryDev: identity.dev,
|
|
200
|
+
directoryIno: identity.ino,
|
|
201
|
+
};
|
|
202
|
+
const manifest: OwnerManifest = readOwnerManifest(rootStore, existingLocator);
|
|
203
|
+
if (manifest.sessionId !== sessionId) throw new Error("task_artifact_owner_session_mismatch");
|
|
204
|
+
const restoredLocator = locatorFromManifest(manifest);
|
|
205
|
+
const restoredStore = openOwnerStore(context, rootStore, restoredLocator, sessionId);
|
|
206
|
+
result = taskArtifactOwnerFromStore(restoredLocator, restoredStore);
|
|
207
|
+
} else {
|
|
208
|
+
let legacySource: { relativePath: string; snapshot: NativeDirectoryTreeSnapshot } | undefined;
|
|
209
|
+
if (legacyArtifactRelativePath !== undefined) {
|
|
210
|
+
assertSafeRelativePath(legacyArtifactRelativePath);
|
|
211
|
+
if (legacyPathOverlapsOwner(context.sessionsRoot, legacyArtifactRelativePath, ownerRelative))
|
|
212
|
+
throw new Error("task_artifact_owner_legacy_path_overlaps_owner");
|
|
213
|
+
try {
|
|
214
|
+
fs.lstatSync(path.join(context.sessionsRoot, legacyArtifactRelativePath), { bigint: true });
|
|
215
|
+
legacySource = {
|
|
216
|
+
relativePath: legacyArtifactRelativePath,
|
|
217
|
+
snapshot: rootStore.captureTree(legacyArtifactRelativePath),
|
|
218
|
+
};
|
|
219
|
+
} catch (error) {
|
|
220
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
rootStore.ensureDirectory(OWNER_DIRECTORY);
|
|
225
|
+
const identity = rootStore.ensureDirectory(ownerRelative);
|
|
226
|
+
const createdLocator: TaskArtifactOwnerLocator = {
|
|
227
|
+
schemaVersion: OWNER_SCHEMA_VERSION,
|
|
228
|
+
ownerId,
|
|
229
|
+
directoryDev: identity.dev.toString(),
|
|
230
|
+
directoryIno: identity.ino.toString(),
|
|
231
|
+
};
|
|
232
|
+
ownerStore = rootStore.deriveSubtree(ownerRelative);
|
|
233
|
+
if (legacySource) {
|
|
234
|
+
await copyLegacyArtifactTree(
|
|
235
|
+
rootStore,
|
|
236
|
+
legacySource.relativePath,
|
|
237
|
+
ownerRelative,
|
|
238
|
+
legacySource.snapshot,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
ownerStore.fsyncTree();
|
|
242
|
+
lock.assertOwned();
|
|
243
|
+
await ownerStore.publishNoReplace(
|
|
244
|
+
OWNER_MANIFEST,
|
|
245
|
+
Buffer.from(`${JSON.stringify({ ...createdLocator, sessionId })}\n`, "utf8"),
|
|
246
|
+
);
|
|
247
|
+
const verifiedStore = openOwnerStore(context, rootStore, createdLocator, sessionId);
|
|
248
|
+
try {
|
|
249
|
+
ownerStore.close();
|
|
250
|
+
} catch (error) {
|
|
251
|
+
try {
|
|
252
|
+
verifiedStore.close();
|
|
253
|
+
} catch (closeError) {
|
|
254
|
+
throw new AggregateError(
|
|
255
|
+
[error, closeError],
|
|
256
|
+
"Opening the owner and closing its writer store failed.",
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
throw error;
|
|
260
|
+
}
|
|
261
|
+
ownerStore = undefined;
|
|
262
|
+
result = taskArtifactOwnerFromStore(createdLocator, verifiedStore);
|
|
263
|
+
}
|
|
264
|
+
} catch (error) {
|
|
265
|
+
recordFailure(state, error, "Provisioning the task artifact owner failed.");
|
|
266
|
+
}
|
|
267
|
+
closeManagedStore(
|
|
268
|
+
ownerStore,
|
|
269
|
+
state,
|
|
270
|
+
"Provisioning the task artifact owner and closing its writer store failed.",
|
|
271
|
+
);
|
|
272
|
+
closeManagedStore(rootStore, state, "Provisioning the task artifact owner and closing its root store failed.");
|
|
273
|
+
}
|
|
274
|
+
} catch (error) {
|
|
275
|
+
recordFailure(state, error, "Task artifact owner setup failed.");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
await lock.release();
|
|
280
|
+
} catch (releaseError) {
|
|
281
|
+
recordFailure(state, releaseError, "Task artifact owner setup and lock release failed.");
|
|
282
|
+
}
|
|
283
|
+
if (state.failed) {
|
|
284
|
+
closeReturnedOwner(result, state);
|
|
285
|
+
throw state.value;
|
|
286
|
+
}
|
|
287
|
+
if (!result) throw new Error("task_artifact_owner_unavailable");
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
@@ -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;
|