@aliyunrds/ctxdb 1.0.8 → 1.0.9-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,7 +11,7 @@ Unified access layer for RDS ContextDatabase. One `ctxdb` CLI (memory + KB ops),
11
11
  | **claude** | ✅ UserPromptSubmit + Stop + SessionStart | `~/.claude/skills/ctxdb/` | hooks-driven (auto capture/recall) |
12
12
  | **opencode** | Plugin shim (`~/.config/opencode/plugins/ctxdb.ts`) | `~/.config/opencode/skills/` | in-process plugin (auto capture/recall + KB catalog injection) |
13
13
  | **hermes** | ✅ `pre_llm_call` + `post_llm_call` | `~/.hermes/skills/` | shell hooks (auto capture/recall + first-turn warmup) |
14
- | **workbuddy** | ✅ UserPromptSubmit + Stop + SessionStart | `~/.workbuddy-ai/skills/ctxdb/` | hooks-driven (auto capture/recall); Claude-Code-isomorphic (exec form) |
14
+ | **workbuddy** | ✅ UserPromptSubmit + Stop + SessionStart | `~/.workbuddy/skills/` and `~/.workbuddy-ai/skills/` (existing homes) | CodeBuddy command hooks (complete shell command, no separate `args`) |
15
15
 
16
16
  Config and logs live under `~/.ctxdb/`. `~/.ctxdb/ctxdb.json` is one file, but runtime config is isolated per agent under `agents.qoder`, `agents.qoderwork`, `agents.qwenwork`, `agents.codex`, `agents.claude`, `agents.opencode`, `agents.hermes`, and `agents.workbuddy`. (Pre-2026-05-23 installs used `~/.ctxdb.json` at the home root; running `ctxdb setup --agent <name>` migrates the file into the selected agent section.)
17
17
 
@@ -105,7 +105,11 @@ URLs never come from server discovery. When a console URL is available, login op
105
105
  browser and keeps polling. It always prints the URL and code; if opening fails, use that URL
106
106
  manually. Pass `--no-browser` to print the link without opening a browser.
107
107
 
108
- POSIX uses private files/directories; Windows validates an owner-only ACL using local PowerShell.
108
+ Credential storage trusts the user's local environment and relies on OS access control.
109
+ New POSIX directories/files request modes `0700`/`0600`; Windows uses normal inherited permissions.
110
+ Reads and writes do not reject an accessible file based on its owner, mode bits or ACL, and do not
111
+ invoke PowerShell to inspect or change permissions. File type/symlink checks, JSON validation,
112
+ atomic replacement and concurrency locks still apply.
109
113
  Native Windows acceptance remains separate from the macOS local test record.
110
114
 
111
115
  ### Internal browser-login distribution
@@ -117,7 +121,7 @@ and `ctxdb auth status`. The default login uses a browser loopback callback;
117
121
  provider-neutral protocol and use PKCE. The selected Workspace is represented
118
122
  by the returned API key, so the CLI does not persist a separate Workspace id.
119
123
 
120
- Internal login stores its single active connection in owner-only
124
+ Internal login stores its single active connection in
121
125
  `~/.ctxdb/credentials.json`. It then runs the normal Agent setup lifecycle
122
126
  without writing an `api_key` field into the Agent or default profile; an
123
127
  existing field in either updated profile is removed. At runtime this managed
@@ -303,7 +307,7 @@ ctxdb setup --agent codex --api-key 'ctxdb-new-...'
303
307
  ctxdb status --agent codex --json
304
308
  ```
305
309
 
306
- Setup first checks that the target agent home exists (`~/.qoder`, `~/.qoderwork`, `~/.qwenwork`, `~/.codex`, `~/.claude`, or `~/.workbuddy-ai`; the declared CN variant home also satisfies this check). Setup writes hooks and skills only into homes that already exist, so a CN-only install does not create the international product home, and vice versa. If no home exists, install or start that agent once before running `ctxdb setup --agent <name>`. OpenCode and Hermes are config-dir style: setup creates `~/.config/opencode` / `~/.hermes` when they are missing.
310
+ Setup first checks that the target agent home exists (`~/.qoder`, `~/.qoderwork`, `~/.qwenwork`, `~/.codex`, `~/.claude`, or either WorkBuddy home: `~/.workbuddy` / `~/.workbuddy-ai`; other declared CN variant homes also satisfy this check). Setup writes hooks and skills into every existing home, so a CN-only install does not create the international product home, and vice versa. If no home exists, install or start that agent once before running `ctxdb setup --agent <name>`. OpenCode and Hermes are config-dir style: setup creates `~/.config/opencode` / `~/.hermes` when they are missing.
307
311
 
308
312
  For **qoder**, **qoderwork**, **qwenwork**, **codex**, **claude**, and **workbuddy**, restart the harness (CLI: just exit + restart; app: Cmd+R or quit/relaunch) so it picks up the new hooks/skill. For **codex**, setup also writes `[features].hooks = true` into `~/.codex/config.toml` (creating the file if missing) — Codex won't fire any hook entries without it. On first use Codex may prompt you to trust the new hook commands. For **opencode**, restart OpenCode so it loads `~/.config/opencode/plugins/ctxdb.ts`. For **hermes**, setup also checks `~/.hermes/shell-hooks-allowlist.json`; it reports setup as incomplete until both ctxdb hook commands are approved.
309
313
 
@@ -870,7 +874,7 @@ ctxdb uninstall --purge-logs # also deletes ~/.ctxdb/logs/
870
874
 
871
875
  ### What uninstall does (and doesn't) touch
872
876
 
873
- - **Hook entries in `~/.qoder/settings.json`, `~/.qoderwork/settings.json`, `~/.qwenwork/settings.json`, `~/.codex/hooks.json`, `~/.claude/settings.json`, `~/.workbuddy-ai/settings.json`, and `~/.hermes/config.yaml`**: stripped precisely by marker (`_ctxdb = @aliyunrds/ctxdb`, plus legacy keys `_ctxdbQoder` / `_ctxdbPackage` and the legacy `@aliyunrds/ctxdb-qoder` value for installs predating the unified marker). Any hooks you added yourself stay. A timestamped `*.bak-ctxdb-<TS>` is written before each modification (rotation keeps the 5 most recent).
877
+ - **Hook entries in `~/.qoder/settings.json`, `~/.qoderwork/settings.json`, `~/.qwenwork/settings.json`, `~/.codex/hooks.json`, `~/.claude/settings.json`, `~/.workbuddy/settings.json`, `~/.workbuddy-ai/settings.json`, and `~/.hermes/config.yaml`**: stripped precisely by marker (`_ctxdb = @aliyunrds/ctxdb`, plus legacy keys `_ctxdbQoder` / `_ctxdbPackage` and the legacy `@aliyunrds/ctxdb-qoder` value for installs predating the unified marker). Any hooks you added yourself stay. A timestamped `*.bak-ctxdb-<TS>` is written before each modification (rotation keeps the 5 most recent).
874
878
  - **Skill directories**: `~/.<agent>/skills/ctxdb/` for each set-up agent, plus legacy dirs (`ctxdb-qoder`, `rds-ctxdb-qoder`, `qoder-ctxdb`) under `~/.qoder/skills/` from older package names.
875
879
  - **`~/.ctxdb/` state**: only with `--purge-config` / `--purge-logs` / `--purge-all`. Defensive: `--purge-all` removes the `~/.ctxdb/` root only if it's empty after the named files are deleted (won't blanket-rm an unknown directory).
876
880
  - **`~/.codex/config.toml` `[features].hooks`**: **NOT** reverted. Setup adds `hooks = true` so Codex will fire ctxdb's hook entries; uninstall leaves the flag alone because (a) the user may have wanted it on for non-ctxdb hooks, and (b) it's harmless when `~/.codex/hooks.json` is empty. If you want it off, edit the file by hand.
@@ -456,7 +456,8 @@ function agentHomeDir(agent, home = homedir2()) {
456
456
  var AGENT_VARIANT_HOMES = {
457
457
  qoder: [".qoder-cn"],
458
458
  qoderwork: [".qoderworkcn"],
459
- qwenwork: [".qwenworkcn"]
459
+ qwenwork: [".qwenworkcn"],
460
+ workbuddy: [".workbuddy"]
460
461
  };
461
462
  function agentHomeDirs(agent, home = homedir2()) {
462
463
  const primary = agentHomeDir(agent, home);
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  configDir
4
- } from "./chunk-3FAQREIU.js";
4
+ } from "./chunk-2U7G6UXE.js";
5
5
 
6
6
  // src/lib/circuit.ts
7
7
  import { statSync, writeFileSync, unlinkSync, mkdirSync, readdirSync, readFileSync } from "fs";
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  fetchKbCatalogBlock,
4
4
  recallTurn
5
- } from "./chunk-AAWG5SPO.js";
5
+ } from "./chunk-V7IZ2UYJ.js";
6
6
  import {
7
7
  debug
8
- } from "./chunk-3FAQREIU.js";
8
+ } from "./chunk-2U7G6UXE.js";
9
9
 
10
10
  // src/lib/warmup-recall.ts
11
11
  import { execSync } from "child_process";
@@ -4,13 +4,13 @@ import {
4
4
  isConnectionError,
5
5
  resetCircuit,
6
6
  tripCircuit
7
- } from "./chunk-MWAFAK5M.js";
7
+ } from "./chunk-7TPYIT63.js";
8
8
  import {
9
9
  CtxdbError,
10
10
  debug,
11
11
  isDataApiReady,
12
12
  isDebug
13
- } from "./chunk-3FAQREIU.js";
13
+ } from "./chunk-2U7G6UXE.js";
14
14
 
15
15
  // src/lib/capture-orchestrator.ts
16
16
  import {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CtxdbError
4
- } from "./chunk-3FAQREIU.js";
4
+ } from "./chunk-2U7G6UXE.js";
5
5
 
6
6
  // src/lib/kb.ts
7
7
  import {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  fetchKbCatalogBlock,
4
4
  recallTurn
5
- } from "./chunk-AAWG5SPO.js";
5
+ } from "./chunk-V7IZ2UYJ.js";
6
6
 
7
7
  // src/lib/user-prompt-submit-compose.ts
8
8
  async function composeUserPromptSubmit(cfg, agent, client, prompt, sessionId = null) {
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  listKnowledgeBases
4
- } from "./chunk-LZD55CWE.js";
4
+ } from "./chunk-T5ZJEPR4.js";
5
5
  import {
6
6
  isConnectionError,
7
7
  resetCircuit,
8
8
  tripCircuit
9
- } from "./chunk-MWAFAK5M.js";
9
+ } from "./chunk-7TPYIT63.js";
10
10
  import {
11
11
  CtxdbError,
12
12
  isDataApiReady,
13
13
  resolveDataApiCredential
14
- } from "./chunk-3FAQREIU.js";
14
+ } from "./chunk-2U7G6UXE.js";
15
15
 
16
16
  // src/lib/kb-catalog.ts
17
17
  function sanitizeKeyEntities(raw) {
package/dist/cli/main.js CHANGED
@@ -31,7 +31,7 @@ import {
31
31
  uploadFile,
32
32
  uploadText,
33
33
  validateLocalFile
34
- } from "../chunk-LZD55CWE.js";
34
+ } from "../chunk-T5ZJEPR4.js";
35
35
  import {
36
36
  AGENT_CHOICES,
37
37
  CtxdbError,
@@ -61,7 +61,7 @@ import {
61
61
  save,
62
62
  updateConfiguredDebug,
63
63
  writeInstalledPkgVersion
64
- } from "../chunk-3FAQREIU.js";
64
+ } from "../chunk-2U7G6UXE.js";
65
65
  import {
66
66
  beginUpdateNotification,
67
67
  completeUpdateNotification,
@@ -402,6 +402,17 @@ import { fileURLToPath } from "url";
402
402
  import { execFileSync } from "child_process";
403
403
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
404
404
  import { setupAuthentication, oauthProfileStatus, validateDataBaseUrl } from "@aliyunrds/ctxdb-shared";
405
+
406
+ // src/setup/workbuddy-command.ts
407
+ function workbuddyHookCommand(nodePath, scriptPath, platform2 = process.platform) {
408
+ const quotePath = (path) => {
409
+ const normalized = platform2 === "win32" ? path.replace(/\\/g, "/") : path;
410
+ return `"${normalized.replace(/[\\"$`]/g, "\\$&")}"`;
411
+ };
412
+ return `${quotePath(nodePath)} ${quotePath(scriptPath)} --agent=workbuddy`;
413
+ }
414
+
415
+ // src/setup/installer.ts
405
416
  var PACKAGE_UNINSTALL_TARGETS = DISTRIBUTION_MANIFEST.id === "internal" ? DISTRIBUTION_MANIFEST.packageName : `${PACKAGE_NAME} @aliyunrds/ctxdb-shared`;
406
417
  var UNINSTALL_NPM_HINT = `To also remove the npm binaries: \`npm uninstall -g ${PACKAGE_UNINSTALL_TARGETS}\``;
407
418
  var UNINSTALL_ORDER_HINT = `When you eventually want to uninstall: run \`ctxdb uninstall --purge-all\` FIRST, then \`npm uninstall -g ${PACKAGE_UNINSTALL_TARGETS}\`. (npm 7+ removed uninstall lifecycle hooks, so the order matters \u2014 otherwise agent settings.json hooks + ~/.ctxdb/ residue stay behind.)`;
@@ -1301,8 +1312,8 @@ function checkHookNodePaths(agent) {
1301
1312
  const cmd = h.command;
1302
1313
  let nodePath;
1303
1314
  if (cmd.startsWith('"')) {
1304
- const closeQuote = cmd.indexOf('"', 1);
1305
- if (closeQuote > 1) nodePath = cmd.slice(1, closeQuote);
1315
+ const quoted = /^"((?:\\.|[^"\\])*)"/.exec(cmd);
1316
+ if (quoted) nodePath = quoted[1].replace(/\\([\\"$`])/g, "$1");
1306
1317
  } else {
1307
1318
  const parts = cmd.split(" ");
1308
1319
  if (parts.length >= 2 && !parts[0].endsWith(".js") && !parts[0].endsWith(".ts") && (parts[0].includes("/") || parts[0].includes("\\"))) {
@@ -1664,9 +1675,9 @@ var LEGACY_MARKER_VALUES = ["@aliyunrds/ctxdb-qoder"];
1664
1675
  var TOOL_SCOPED_EVENTS = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUse"]);
1665
1676
  function appendOne(hooks, event, command, agent) {
1666
1677
  if (!Array.isArray(hooks[event])) hooks[event] = [];
1667
- const useExecForm = agent === "claude" || agent === "workbuddy";
1678
+ const useExecForm = agent === "claude";
1668
1679
  const cmdRunner = agent === "qoder";
1669
- const shellCmd = process.platform === "win32" ? cmdRunner ? `node ${command.replace(/\\/g, "/")} --agent=${agent}` : `"${process.execPath.replace(/\\/g, "/")}" "${command.replace(/\\/g, "/")}" --agent=${agent}` : `${process.execPath} ${command} --agent=${agent}`;
1680
+ const shellCmd = agent === "workbuddy" ? workbuddyHookCommand(process.execPath, command) : process.platform === "win32" ? cmdRunner ? `node ${command.replace(/\\/g, "/")} --agent=${agent}` : `"${process.execPath.replace(/\\/g, "/")}" "${command.replace(/\\/g, "/")}" --agent=${agent}` : `${process.execPath} ${command} --agent=${agent}`;
1670
1681
  const dup = hooks[event].some(
1671
1682
  (entry2) => Array.isArray(entry2?.hooks) && entry2.hooks.some(
1672
1683
  (h) => h?.type === "command" && (Array.isArray(h.args) && h.args[0] === command || typeof h.command === "string" && h.command.includes(command))
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  captureParsedMessages
4
- } from "../chunk-YTCXHO4F.js";
5
- import "../chunk-MWAFAK5M.js";
4
+ } from "../chunk-N5JGS5EC.js";
5
+ import "../chunk-7TPYIT63.js";
6
6
  import {
7
7
  HttpClient,
8
8
  agentFromArgvWithFallback,
@@ -10,7 +10,7 @@ import {
10
10
  load,
11
11
  resolveDataApiCredential,
12
12
  setDebug
13
- } from "../chunk-3FAQREIU.js";
13
+ } from "../chunk-2U7G6UXE.js";
14
14
  import {
15
15
  shouldSkipHooks
16
16
  } from "../chunk-5ML4VSXN.js";
@@ -2,17 +2,17 @@
2
2
  import {
3
3
  HOOK_TIMEOUT_MS,
4
4
  composeSessionStart
5
- } from "../chunk-RRDQCZJ4.js";
5
+ } from "../chunk-FD3IPF6R.js";
6
6
  import {
7
7
  composeUserPromptSubmit
8
- } from "../chunk-AH2QEUK4.js";
8
+ } from "../chunk-U4DRH4NZ.js";
9
9
  import {
10
10
  fetchKbCatalogBlock
11
- } from "../chunk-AAWG5SPO.js";
12
- import "../chunk-LZD55CWE.js";
11
+ } from "../chunk-V7IZ2UYJ.js";
12
+ import "../chunk-T5ZJEPR4.js";
13
13
  import {
14
14
  isCircuitOpen
15
- } from "../chunk-MWAFAK5M.js";
15
+ } from "../chunk-7TPYIT63.js";
16
16
  import {
17
17
  HttpClient,
18
18
  agentFromArgvWithFallback,
@@ -20,7 +20,7 @@ import {
20
20
  load,
21
21
  resolveDataApiCredential,
22
22
  setDebug
23
- } from "../chunk-3FAQREIU.js";
23
+ } from "../chunk-2U7G6UXE.js";
24
24
  import {
25
25
  shouldSkipHooks
26
26
  } from "../chunk-5ML4VSXN.js";
@@ -3,12 +3,12 @@ import {
3
3
  HOOK_TIMEOUT_MS,
4
4
  composeSessionStart,
5
5
  formatSessionStartStdout
6
- } from "../chunk-RRDQCZJ4.js";
7
- import "../chunk-AAWG5SPO.js";
8
- import "../chunk-LZD55CWE.js";
6
+ } from "../chunk-FD3IPF6R.js";
7
+ import "../chunk-V7IZ2UYJ.js";
8
+ import "../chunk-T5ZJEPR4.js";
9
9
  import {
10
10
  isCircuitOpen
11
- } from "../chunk-MWAFAK5M.js";
11
+ } from "../chunk-7TPYIT63.js";
12
12
  import {
13
13
  HttpClient,
14
14
  agentFromArgvWithFallback,
@@ -16,7 +16,7 @@ import {
16
16
  load,
17
17
  resolveDataApiCredential,
18
18
  setDebug
19
- } from "../chunk-3FAQREIU.js";
19
+ } from "../chunk-2U7G6UXE.js";
20
20
  import {
21
21
  shouldSkipHooks
22
22
  } from "../chunk-5ML4VSXN.js";
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  captureTurn
4
- } from "../chunk-YTCXHO4F.js";
5
- import "../chunk-MWAFAK5M.js";
4
+ } from "../chunk-N5JGS5EC.js";
5
+ import "../chunk-7TPYIT63.js";
6
6
  import {
7
7
  HttpClient,
8
8
  agentFromArgvWithFallback,
@@ -10,7 +10,7 @@ import {
10
10
  load,
11
11
  resolveDataApiCredential,
12
12
  setDebug
13
- } from "../chunk-3FAQREIU.js";
13
+ } from "../chunk-2U7G6UXE.js";
14
14
  import {
15
15
  shouldSkipHooks
16
16
  } from "../chunk-5ML4VSXN.js";
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  composeUserPromptSubmit,
4
4
  formatUserPromptSubmitStdout
5
- } from "../chunk-AH2QEUK4.js";
6
- import "../chunk-AAWG5SPO.js";
7
- import "../chunk-LZD55CWE.js";
5
+ } from "../chunk-U4DRH4NZ.js";
6
+ import "../chunk-V7IZ2UYJ.js";
7
+ import "../chunk-T5ZJEPR4.js";
8
8
  import {
9
9
  isCircuitOpen
10
- } from "../chunk-MWAFAK5M.js";
10
+ } from "../chunk-7TPYIT63.js";
11
11
  import {
12
12
  HttpClient,
13
13
  agentFromArgvWithFallback,
@@ -15,7 +15,7 @@ import {
15
15
  load,
16
16
  resolveDataApiCredential,
17
17
  setDebug
18
- } from "../chunk-3FAQREIU.js";
18
+ } from "../chunk-2U7G6UXE.js";
19
19
  import {
20
20
  shouldSkipHooks
21
21
  } from "../chunk-5ML4VSXN.js";
@@ -1028,88 +1028,28 @@ import { randomBytes as randomBytes3 } from "crypto";
1028
1028
  // ../shared/src/credentials/local-credential-provider.ts
1029
1029
  import { createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
1030
1030
  import { homedir as homedir2 } from "os";
1031
- import { join as join3 } from "path";
1031
+ import { join as join2 } from "path";
1032
1032
 
1033
1033
  // ../shared/src/credentials/storage.ts
1034
1034
  import { constants, closeSync as closeSync2, existsSync as existsSync2, fstatSync, fsyncSync, lstatSync, mkdirSync as mkdirSync2, openSync as openSync2, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
1035
1035
  import { randomBytes, createHash } from "crypto";
1036
1036
  import { dirname as dirname2 } from "path";
1037
1037
  import { setTimeout as delay2 } from "timers/promises";
1038
-
1039
- // ../shared/src/windows-private-storage.ts
1040
- import { execFileSync } from "child_process";
1041
- import { join as join2 } from "path";
1042
- var SCRIPT = `
1043
- $ErrorActionPreference = 'Stop'
1044
- $path = $env:CTXDB_PRIVATE_STORAGE_PATH
1045
- $sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
1046
- $item = Get-Item -LiteralPath $path -Force
1047
- if (($item.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) { throw 'reparse' }
1048
- if ($env:CTXDB_PRIVATE_STORAGE_INITIALIZE -eq '1') {
1049
- if (-not $item.PSIsContainer) { throw 'not directory' }
1050
- $acl = [System.Security.AccessControl.DirectorySecurity]::new()
1051
- $acl.SetOwner($sid)
1052
- $acl.SetAccessRuleProtection($true, $false)
1053
- $rule = [System.Security.AccessControl.FileSystemAccessRule]::new($sid, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')
1054
- $acl.AddAccessRule($rule)
1055
- Set-Acl -LiteralPath $path -AclObject $acl
1056
- }
1057
- $acl = Get-Acl -LiteralPath $path
1058
- if ($acl.GetOwner([System.Security.Principal.SecurityIdentifier]).Value -ne $sid.Value) { throw 'owner' }
1059
- $self = $false
1060
- foreach ($rule in $acl.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])) {
1061
- if ($rule.AccessControlType -eq 'Allow') {
1062
- if ($rule.IdentityReference.Value -ne $sid.Value) { throw 'other principal' }
1063
- if (($rule.FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::FullControl) -eq [System.Security.AccessControl.FileSystemRights]::FullControl) { $self = $true }
1064
- }
1065
- }
1066
- if (-not $self) { throw 'missing owner access' }
1067
- Write-Output 'private'
1068
- `;
1069
- function checkWindowsPrivateStorage(path, initialize, run = execFileSync) {
1070
- const systemRoot = process.env.SystemRoot;
1071
- if (!systemRoot) throw new Error("Private Windows credential storage could not be verified.");
1072
- try {
1073
- const result = run(
1074
- join2(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe"),
1075
- ["-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(SCRIPT, "utf16le").toString("base64")],
1076
- {
1077
- env: { ...process.env, CTXDB_PRIVATE_STORAGE_PATH: path, CTXDB_PRIVATE_STORAGE_INITIALIZE: initialize ? "1" : "0" },
1078
- encoding: "utf8",
1079
- windowsHide: true,
1080
- timeout: 1e4,
1081
- maxBuffer: 4096,
1082
- stdio: ["ignore", "pipe", "pipe"]
1083
- }
1084
- );
1085
- if (String(result).trim() !== "private") throw new Error();
1086
- } catch {
1087
- throw new Error("Private Windows credential storage could not be verified.");
1088
- }
1089
- }
1090
-
1091
- // ../shared/src/credentials/storage.ts
1092
- function privatePath(path, directory = false) {
1038
+ function assertStoragePath(path, directory = false) {
1093
1039
  const s = lstatSync(path);
1094
- if (s.isSymbolicLink() || (directory ? !s.isDirectory() : !s.isFile())) throw new Error("credentials: storage is not private or is invalid");
1095
- if (process.platform === "win32") {
1096
- checkWindowsPrivateStorage(path, false);
1097
- return;
1098
- }
1099
- if (s.mode & 63 || process.getuid && s.uid !== process.getuid()) throw new Error("credentials: storage must be private and owner-only (run chmod 600 on credentials.json)");
1040
+ if (s.isSymbolicLink() || (directory ? !s.isDirectory() : !s.isFile())) throw new Error("credentials: invalid storage path");
1100
1041
  }
1101
1042
  function prepareStorage(path) {
1102
1043
  const dir = dirname2(path);
1103
1044
  if (!existsSync2(dir)) {
1104
1045
  mkdirSync2(dir, { recursive: true, mode: 448 });
1105
- if (process.platform === "win32") checkWindowsPrivateStorage(dir, true);
1106
1046
  }
1107
- privatePath(dir, true);
1047
+ assertStoragePath(dir, true);
1108
1048
  }
1109
- function readPrivate(path) {
1049
+ function readCredentialFile(path) {
1110
1050
  if (!existsSync2(path)) return void 0;
1111
- privatePath(dirname2(path), true);
1112
- privatePath(path);
1051
+ assertStoragePath(dirname2(path), true);
1052
+ assertStoragePath(path);
1113
1053
  const fd = openSync2(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
1114
1054
  try {
1115
1055
  const stat = fstatSync(fd);
@@ -1121,7 +1061,7 @@ function readPrivate(path) {
1121
1061
  }
1122
1062
  function atomicCredentialWrite(path, value) {
1123
1063
  prepareStorage(path);
1124
- if (existsSync2(path)) privatePath(path);
1064
+ if (existsSync2(path)) assertStoragePath(path);
1125
1065
  const temp = path + "." + randomBytes(12).toString("hex") + ".tmp";
1126
1066
  let fd;
1127
1067
  try {
@@ -1203,7 +1143,7 @@ var ACTIVE_CONTEXTDB_CREDENTIAL = "contextdb/active";
1203
1143
 
1204
1144
  // ../shared/src/credentials/local-credential-provider.ts
1205
1145
  function defaultCredentialsPath() {
1206
- return join3(homedir2(), ".ctxdb", "credentials.json");
1146
+ return join2(homedir2(), ".ctxdb", "credentials.json");
1207
1147
  }
1208
1148
  var object2 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
1209
1149
  var fail = (message) => {
@@ -1239,7 +1179,7 @@ function validate(key, raw) {
1239
1179
  if (raw.kind === "api-key") validateApi(raw, key === ACTIVE_CONTEXTDB_CREDENTIAL);
1240
1180
  }
1241
1181
  function readDocument(path) {
1242
- const bytes = readPrivate(path);
1182
+ const bytes = readCredentialFile(path);
1243
1183
  if (bytes === void 0) return { version: 2, records: {} };
1244
1184
  let doc;
1245
1185
  try {
@@ -1421,7 +1361,7 @@ var OAuthCredentialError = class extends Error {
1421
1361
  login_required: "OAuth session needs login; run ctxdb login for this profile.",
1422
1362
  credential_busy: "OAuth credential is busy. If its previous process crashed, run ctxdb login for a new credential.",
1423
1363
  invalid_profile: "OAuth credential does not belong to this profile.",
1424
- unsafe_storage: "OAuth credential storage is not private or is invalid."
1364
+ unsafe_storage: "OAuth credential storage is inaccessible or invalid."
1425
1365
  };
1426
1366
  super(messages[code]);
1427
1367
  this.name = "OAuthCredentialError";
@@ -1664,7 +1604,7 @@ function authorization(session) {
1664
1604
  }
1665
1605
 
1666
1606
  // ../shared/src/oauth-profile.ts
1667
- import { dirname as dirname4, join as join4, resolve } from "path";
1607
+ import { dirname as dirname4, join as join3, resolve } from "path";
1668
1608
 
1669
1609
  // ../shared/src/auth-config.ts
1670
1610
  import { closeSync as closeSync3, existsSync as existsSync3, fsyncSync as fsyncSync2, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync3, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
@@ -1722,7 +1662,7 @@ function oauthProfile(reference, configPath, profile, baseUrl) {
1722
1662
  reference,
1723
1663
  profile,
1724
1664
  configPath: path,
1725
- credentialsPath: join4(dirname4(path), "credentials.json"),
1665
+ credentialsPath: join3(dirname4(path), "credentials.json"),
1726
1666
  baseUrl: normalizeCoreOrigin(baseUrl)
1727
1667
  };
1728
1668
  }
@@ -1789,10 +1729,10 @@ var OAuthProfileProvider = class {
1789
1729
 
1790
1730
  // ../shared/src/login-bindings.ts
1791
1731
  import { randomBytes as randomBytes5 } from "crypto";
1792
- import { dirname as dirname6, join as join6 } from "path";
1732
+ import { dirname as dirname6, join as join5 } from "path";
1793
1733
 
1794
1734
  // ../shared/src/data-auth.ts
1795
- import { dirname as dirname5, join as join5 } from "path";
1735
+ import { dirname as dirname5, join as join4 } from "path";
1796
1736
  function selectDataApiCredential(cfg, env = process.env) {
1797
1737
  const accessToken = env.CTXDB_ACCESS_TOKEN?.trim();
1798
1738
  if (accessToken) return { type: "access-token", value: accessToken };
@@ -1810,7 +1750,7 @@ var DataApiAuthorizationProvider = class {
1810
1750
  const c = this.context, raw = readAuthConfig(c.configPath), section = ownAuthProfile(raw, c.agent);
1811
1751
  const fallback = c.inheritDefaultKey && c.agent !== "default" ? ownAuthProfile(raw, "default") : {};
1812
1752
  if (c.managedCredentials) {
1813
- const active = readActiveCredentialSync(c.credentialsPath ?? join5(dirname5(c.configPath), "credentials.json"));
1753
+ const active = readActiveCredentialSync(c.credentialsPath ?? join4(dirname5(c.configPath), "credentials.json"));
1814
1754
  if (active) {
1815
1755
  const baseUrl2 = c.baseUrlOverride ?? active.metadata.baseUrl;
1816
1756
  if (baseUrl2 !== c.baseUrl) throw new OAuthCredentialError("login_required");
@@ -1836,7 +1776,7 @@ function publicAuthenticationFromConfig(raw, path, agent, baseUrlOverride) {
1836
1776
  const oauthCredential = oauthProfileFromConfig(raw, path, agent, baseUrlOverride);
1837
1777
  return oauthCredential ? { baseUrl: oauthCredential.baseUrl, oauthCredential } : void 0;
1838
1778
  }
1839
- const r = new LocalCredentialProvider(join5(dirname5(path), "credentials.json")).readStoredRecord(section.credential_ref);
1779
+ const r = new LocalCredentialProvider(join4(dirname5(path), "credentials.json")).readStoredRecord(section.credential_ref);
1840
1780
  if (!r || r.kind !== "api-key" || r.metadata.state !== "ready" || r.metadata.owner.distribution !== "public" || r.metadata.owner.agent !== agent || r.metadata.owner.loginId) {
1841
1781
  throw new AuthConfigError("credential_ref is missing, invalid or belongs to another Agent. Use setup --api-key/--login to choose a valid credential.");
1842
1782
  }
@@ -1859,12 +1799,12 @@ function createRequestAuthorizationProvider(credential, baseUrl) {
1859
1799
  }
1860
1800
 
1861
1801
  // ../shared/src/authorization/public-target.ts
1862
- import { dirname as dirname7, join as join7 } from "path";
1802
+ import { dirname as dirname7, join as join6 } from "path";
1863
1803
 
1864
1804
  // src/config.ts
1865
1805
  import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
1866
1806
  import { homedir as homedir3 } from "os";
1867
- import { dirname as dirname8, join as join8 } from "path";
1807
+ import { dirname as dirname8, join as join7 } from "path";
1868
1808
 
1869
1809
  // src/distribution-capabilities.ts
1870
1810
  var PUBLIC_DISTRIBUTION = {
@@ -1888,7 +1828,7 @@ var DEFAULT_KNOWLEDGE_TOP_K = 6;
1888
1828
  var DEFAULT_KB_CATALOG_INJECTION = "session_start";
1889
1829
  function defaultPath(env) {
1890
1830
  if (env.CTXDB_CONFIG_PATH) return env.CTXDB_CONFIG_PATH;
1891
- return join8(homedir3(), ".ctxdb", "ctxdb.json");
1831
+ return join7(homedir3(), ".ctxdb", "ctxdb.json");
1892
1832
  }
1893
1833
  function coerceInt(v, fallback) {
1894
1834
  if (v === null || v === void 0 || v === "") return fallback;
@@ -1973,7 +1913,7 @@ function loadOpencodeConfig(options = {}) {
1973
1913
  kbCatalogInjection: coerceKbCatalogInjection(section.kb_catalog_injection)
1974
1914
  };
1975
1915
  const managed = DISTRIBUTION_MANIFEST.capabilities.managedCredentials && options.managedCredentials !== false ? readActiveCredentialSync(
1976
- options.credentialsPath ?? join8(dirname8(path), "credentials.json")
1916
+ options.credentialsPath ?? join7(dirname8(path), "credentials.json")
1977
1917
  ) : null;
1978
1918
  if (managed) {
1979
1919
  cfg.apiKey = managed.payload.apiKey;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aliyunrds/ctxdb",
3
- "version": "1.0.8",
3
+ "version": "1.0.9-beta.0",
4
4
  "type": "module",
5
5
  "description": "Unified access layer for RDS ContextDatabase: `ctxdb` CLI (memory + KB ops), one-shot multi-agent installer, per-agent config, hooks/plugins, and SKILL.md.",
6
6
  "license": "Apache-2.0",
@@ -34,7 +34,7 @@
34
34
  "dependencies": {
35
35
  "semver": "^7.8.5",
36
36
  "yaml": "^2.9.0",
37
- "@aliyunrds/ctxdb-shared": "~0.0.8"
37
+ "@aliyunrds/ctxdb-shared": "~0.0.9-beta.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^22.15.0",