totemlayer 0.1.97 → 0.1.115

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.
Files changed (51) hide show
  1. package/README.md +21 -6
  2. package/bin/automatic-runtime.mjs +2 -1
  3. package/bin/codex-mission-bridge.mjs +6 -2
  4. package/bin/configured-native-installation.mjs +57 -0
  5. package/bin/connected-agent-service.mjs +5 -1
  6. package/bin/device-login.mjs +3 -1
  7. package/bin/inbox-queue.mjs +8 -5
  8. package/bin/mcp.mjs +18 -5
  9. package/bin/mission-agent-service.mjs +13 -4
  10. package/bin/native/darwin-lifetime +0 -0
  11. package/bin/native/darwin-lifetime.json +1 -1
  12. package/bin/native-claude-login.mjs +131 -0
  13. package/bin/native-claude-receiver.mjs +272 -0
  14. package/bin/native-cli-process.py +105 -0
  15. package/bin/native-harness-environment.mjs +3 -1
  16. package/bin/native-hermes-bridge.mjs +99 -0
  17. package/bin/native-hermes-installation.mjs +9 -0
  18. package/bin/native-hermes-mcp.mjs +107 -0
  19. package/bin/native-hermes-mcp.py +205 -0
  20. package/bin/native-hermes-metadata.py +40 -0
  21. package/bin/native-hermes-process.py +62 -0
  22. package/bin/native-hermes-receiver.mjs +28 -0
  23. package/bin/native-inbox-installation.mjs +144 -0
  24. package/bin/native-inbox-receiver.mjs +133 -0
  25. package/bin/native-kilo-installation.mjs +4 -0
  26. package/bin/native-kilo-receiver.mjs +1 -0
  27. package/bin/native-mcp-source.mjs +244 -0
  28. package/bin/native-opencode-bridge.mjs +108 -0
  29. package/bin/native-opencode-installation.mjs +15 -0
  30. package/bin/native-opencode-mcp.mjs +112 -0
  31. package/bin/native-opencode-receiver.mjs +22 -0
  32. package/bin/native-pi-bridge.mjs +170 -0
  33. package/bin/native-pi-installation.mjs +9 -0
  34. package/bin/native-pi-mcp.mjs +139 -0
  35. package/bin/native-pi-receiver.mjs +28 -0
  36. package/bin/network-retry.mjs +9 -1
  37. package/bin/runtime-installation.mjs +6 -1
  38. package/bin/sdk/errors.d.ts +1 -1
  39. package/bin/sdk/errors.js +1 -1
  40. package/bin/sdk/listener.d.ts +1 -0
  41. package/bin/sdk/listener.js +1 -1
  42. package/bin/sdk/transport.js +1 -1
  43. package/bin/setup-assets/api-manifest.json +1 -1
  44. package/bin/setup-assets/manifest.json +1 -1
  45. package/bin/setup-assets/setup.backup.md +2 -2
  46. package/bin/setup-assets/setup.md +2 -2
  47. package/bin/startup-diagnostics.mjs +19 -2
  48. package/bin/strict-installation.mjs +29 -3
  49. package/bin/supervisor-watchdog.mjs +44 -0
  50. package/bin/windows-private-path.mjs +227 -32
  51. package/package.json +2 -2
@@ -1,6 +1,7 @@
1
1
  import { parseArgs } from "node:util";
2
2
  import { GUIDE_UNAVAILABLE, runStrictSetup } from "./strict-setup.mjs";
3
3
  import { readReleaseSetupGuide } from "./setup-guide.mjs";
4
+ import { NATIVE_INSTALL_PROVIDERS } from "./configured-native-installation.mjs";
4
5
 
5
6
  export const STRICT_INSTALL_HELP = `Install Totem with every required verification
6
7
 
@@ -9,6 +10,15 @@ export const STRICT_INSTALL_HELP = `Install Totem with every required verificati
9
10
  [--device-login --no-browser]
10
11
  totem install --read-guide
11
12
  totem install --status
13
+ totem install --provider pi|hermes|opencode|kilo|claude --native-config /private/binding.json
14
+ --session CURRENT_NATIVE_SESSION --model CURRENT_MODEL [--status] [--json]
15
+ totem install --provider claude --native-config /private/binding.json
16
+ --session CURRENT_NATIVE_SESSION --model CURRENT_MODEL --agent-id UUID --environment PRODUCTION|SANDBOX
17
+ --device-login [--no-browser] [--wait-seconds 1200] [--json]
18
+
19
+ Native profiles use their existing sign-in and a private owner-prepared binding.
20
+ This registers/resumes the exact native receiver; it reports incomplete until live
21
+ receiving, restart, peer and website-owner acceptance are separately verified.
12
22
 
13
23
  Run inside the intended agent's command-capable host. The current native session and
14
24
  model must be verified; an installed client is never selected as a substitute.
@@ -37,6 +47,7 @@ export async function strictInstallationMain(args = [], options = {}) {
37
47
  environment: { type: "string" },
38
48
  "base-url": { type: "string" },
39
49
  session: { type: "string" },
50
+ "native-config": { type: "string" },
40
51
  model: { type: "string" },
41
52
  "wait-seconds": { type: "string", default: "1200" },
42
53
  "read-guide": { type: "boolean" },
@@ -94,8 +105,12 @@ export async function strictInstallationMain(args = [], options = {}) {
94
105
  /tn_(?:live|test)_/.test(values.model))
95
106
  )
96
107
  throw new Error();
97
- if (values.session && !/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/i.test(values.session))
108
+ const sessionPattern = values.provider === "hermes" ? /^\d{8}_\d{6}_[a-f0-9]{6}$/
109
+ : ["opencode", "kilo"].includes(values.provider) ? /^ses_[A-Za-z0-9]{12,80}$/
110
+ : /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/i;
111
+ if (values.session && !sessionPattern.test(values.session))
98
112
  throw new Error();
113
+ if (values["native-config"] && !NATIVE_INSTALL_PROVIDERS.includes(values.provider)) throw new Error();
99
114
  const env = options.env ?? process.env;
100
115
  flags = {
101
116
  provider: values.provider ?? (env.CODEX_THREAD_ID ? "codex" : undefined),
@@ -106,10 +121,12 @@ export async function strictInstallationMain(args = [], options = {}) {
106
121
  model: values.model,
107
122
  waitSeconds,
108
123
  json: values.json === true,
124
+ ...(values["native-config"] ? { nativeConfig: values["native-config"] } : {}),
109
125
  };
110
126
  if (values["device-login"]) flags.deviceLogin = true;
111
127
  if (values["no-browser"]) flags.noBrowser = true;
112
- if (values.status) {
128
+ if (values.status && flags.nativeConfig) flags.status = true;
129
+ else if (values.status) {
113
130
  flags.sessionId ??= env.CODEX_THREAD_ID;
114
131
  const { readStrictSetupJob } = await import("./strict-setup-jobs.mjs");
115
132
  const publicStatus = { ...(await readStrictSetupJob(flags, env)) };
@@ -130,6 +147,15 @@ export async function strictInstallationMain(args = [], options = {}) {
130
147
  return 2;
131
148
  }
132
149
  const write = async (value) => stdout.write(JSON.stringify(value) + "\n");
150
+ if (flags.nativeConfig) {
151
+ try {
152
+ const { installConfiguredNativeReceiver } = await import("./configured-native-installation.mjs");
153
+ await write({ ...await installConfiguredNativeReceiver(flags, { ...options, write }), complete: false });
154
+ } catch (error) {
155
+ await write({ complete: false, state: "incomplete", code: /^[A-Z][A-Z0-9_]{0,79}$/.test(error.code ?? "") ? error.code : "NATIVE_INSTALL_FAILED" });
156
+ }
157
+ return 2;
158
+ }
133
159
  if (!options.adapter && !options.foreground && flags.provider === "codex") {
134
160
  // Return before readiness needs a model turn in this same native chat.
135
161
  const adapter = (await import("./strict-codex-adapter.mjs")).createStrictCodexAdapter(flags, {
@@ -190,7 +216,7 @@ export async function strictInstallationMain(args = [], options = {}) {
190
216
  readGuide: readReleaseSetupGuide,
191
217
  async inspect() {
192
218
  throw Object.assign(new Error(), {
193
- code: flags.provider ? "HOST_ADAPTER_UNSUPPORTED" : "EXACT_NATIVE_HOST_REQUIRED",
219
+ code: ["pi", "hermes", "opencode", "kilo"].includes(flags.provider) ? "NATIVE_CONFIGURATION_REQUIRED" : flags.provider ? "HOST_ADAPTER_UNSUPPORTED" : "EXACT_NATIVE_HOST_REQUIRED",
194
220
  });
195
221
  },
196
222
  });
@@ -0,0 +1,44 @@
1
+ import { join } from "node:path";
2
+ import { privateRead, privateWrite } from "./runtime-installation.mjs";
3
+
4
+ /** The launcher owns this ChildProcess; never discover or signal a saved PID here. */
5
+ export function watchSupervisorChild(child, directory, options = {}) {
6
+ const heartbeatFile = options.heartbeatFile ?? "supervisor.json";
7
+ if (!["supervisor.json", "status.json"].includes(heartbeatFile)) throw new Error("Invalid supervisor heartbeat file.");
8
+ const timeoutMs = options.timeoutMs ?? 300000;
9
+ const started = Date.now();
10
+ let checking = false, stopped = false, terminating = false, force;
11
+ const alive = () => child.exitCode === null && child.signalCode === null;
12
+ const stop = () => {
13
+ stopped = true;
14
+ clearInterval(timer);
15
+ clearTimeout(force);
16
+ child.removeListener("exit", stop);
17
+ child.removeListener("error", stop);
18
+ };
19
+ const timer = setInterval(async () => {
20
+ if (stopped || checking || terminating || !alive()) return;
21
+ checking = true;
22
+ try {
23
+ const state = await privateRead(join(directory, heartbeatFile)).catch(() => null);
24
+ if (stopped || !alive()) return;
25
+ const now = Date.now(), updated = Date.parse(state?.updatedAt);
26
+ // A deliberately paused dedicated inbox is quiet by design. The stored
27
+ // PID must still match the child this launcher owns, never another job.
28
+ if (options.allowPaused === true && state?.pid === child.pid && state.state === "paused") return;
29
+ if (now - started < timeoutMs ||
30
+ (state?.pid === child.pid && Number.isFinite(updated) && updated <= now + 5000 && now - updated < timeoutMs)) return;
31
+ terminating = true;
32
+ void privateWrite(join(directory, "supervisor-watchdog.json"), {
33
+ code: "SUPERVISOR_HEARTBEAT_TIMEOUT", pid: child.pid, updatedAt: new Date(now).toISOString(),
34
+ }).catch(() => {});
35
+ child.kill("SIGTERM");
36
+ force = setTimeout(() => { if (alive()) child.kill("SIGKILL"); }, options.graceMs ?? 5000);
37
+ force.unref();
38
+ } finally { checking = false; }
39
+ }, options.intervalMs ?? 15000);
40
+ timer.unref();
41
+ child.once("exit", stop);
42
+ child.once("error", stop);
43
+ return stop;
44
+ }
@@ -1,9 +1,10 @@
1
- import { execFile } from "node:child_process";
2
- import { promisify } from "node:util";
1
+ import { spawn } from "node:child_process";
2
+ import { createConnection } from "node:net";
3
+ import { randomUUID, timingSafeEqual } from "node:crypto";
4
+ import { StringDecoder } from "node:string_decoder";
3
5
  import { join, resolve } from "node:path";
4
6
  import { lstat } from "node:fs/promises";
5
7
 
6
- const execute = promisify(execFile);
7
8
  export function validateWindowsPrivateAcl(snapshot) {
8
9
  const sid = snapshot?.sid;
9
10
  if (typeof sid !== "string" || !/^S-1-(?:\d+-){1,14}\d+$/.test(sid))
@@ -54,36 +55,230 @@ export function validateWindowsPrivateAcl(snapshot) {
54
55
  throw new Error("The private Windows path or its parent can be replaced by another principal.");
55
56
  }
56
57
 
58
+ // Keep the native process, never its results. Every request reads the current
59
+ // file and all ancestor ACLs again, including after a permission change.
60
+ const inspectionScript = (pipeName) => `$ErrorActionPreference='Stop'
61
+ [Console]::Error.WriteLine('TOTEM_ACL_START')
62
+ function Rules($acl) { @($acl.GetAccessRules($true,$true,[Security.Principal.SecurityIdentifier]) | ForEach-Object { @{sid=$_.IdentityReference.Value;rights=[int64]$_.FileSystemRights;type=$_.AccessControlType.ToString();inheritOnly=[bool]($_.PropagationFlags -band [Security.AccessControl.PropagationFlags]::InheritOnly)} }) }
63
+ $security=[IO.Pipes.PipeSecurity]::new()
64
+ $owner=[Security.Principal.WindowsIdentity]::GetCurrent().User
65
+ $security.SetOwner($owner)
66
+ $security.SetAccessRuleProtection($true,$false)
67
+ $security.AddAccessRule([IO.Pipes.PipeAccessRule]::new($owner,[IO.Pipes.PipeAccessRights]::FullControl,[Security.AccessControl.AccessControlType]::Allow))
68
+ $pipe=[IO.Pipes.NamedPipeServerStream]::new('${pipeName}',[IO.Pipes.PipeDirection]::InOut,1,[IO.Pipes.PipeTransmissionMode]::Byte,[IO.Pipes.PipeOptions]::None,4096,4096,$security)
69
+ [Console]::Error.WriteLine('TOTEM_ACL_PIPE_READY')
70
+ try {
71
+ $pipe.WaitForConnection()
72
+ [Console]::Error.WriteLine('TOTEM_ACL_CONNECTED')
73
+ $hello=[Text.Encoding]::UTF8.GetBytes('{"id":0,"nonce":"'+$env:TOTEM_ACL_CHALLENGE+'"}'+"\`n")
74
+ $pipe.Write($hello,0,$hello.Length)
75
+ [Console]::Error.WriteLine('TOTEM_ACL_HELLO_SENT')
76
+ [byte[]]$buffer=[Array]::CreateInstance([byte],4096)
77
+ [Console]::Error.WriteLine('TOTEM_ACL_BUFFER_READY')
78
+ $pending=''
79
+ function Send-Frame($frame) {
80
+ $bytes=[Text.Encoding]::UTF8.GetBytes(($frame | ConvertTo-Json -Depth 9 -Compress)+"\`n")
81
+ $pipe.Write($bytes,0,$bytes.Length)
82
+ }
83
+ while ($true) {
84
+ while (($end=$pending.IndexOf("\`n")) -lt 0) {
85
+ [Console]::Error.WriteLine('TOTEM_ACL_READING')
86
+ $length=$pipe.Read($buffer,0,$buffer.Length)
87
+ if ($length -eq 0) { return }
88
+ [Console]::Error.WriteLine('TOTEM_ACL_FRAME_READ')
89
+ $pending += [Text.Encoding]::ASCII.GetString($buffer,0,$length)
90
+ if ($pending.Length -gt 65536) { throw 'ACL request too large' }
91
+ }
92
+ $line=$pending.Substring(0,$end)
93
+ $pending=$pending.Substring($end+1)
94
+ [Console]::Error.WriteLine('TOTEM_ACL_REQUEST_RECEIVED')
95
+ $request=$null
96
+ try {
97
+ $request=$line | ConvertFrom-Json
98
+ $path=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($request.path))
99
+ $sid=[Security.Principal.WindowsIdentity]::GetCurrent().User.Value
100
+ $item=Get-Item -LiteralPath $path -Force
101
+ $acl=Get-Acl -LiteralPath $path
102
+ $parents=@(); $parent=Split-Path -LiteralPath $path
103
+ while ($parent) {
104
+ $p=Get-Item -LiteralPath $parent -Force
105
+ $parentAcl=Get-Acl -LiteralPath $parent
106
+ $parents+=@{owner=$parentAcl.GetOwner([Security.Principal.SecurityIdentifier]).Value;reparse=[bool]($p.Attributes -band [IO.FileAttributes]::ReparsePoint);rules=@(Rules $parentAcl)}
107
+ $next=Split-Path -LiteralPath $parent
108
+ if ($next -eq $parent) { break }; $parent=$next
109
+ }
110
+ $snapshot=@{sid=$sid;owner=$acl.GetOwner([Security.Principal.SecurityIdentifier]).Value;reparse=[bool]($item.Attributes -band [IO.FileAttributes]::ReparsePoint);rules=@(Rules $acl);ancestors=$parents}
111
+ Send-Frame @{id=$request.id;snapshot=$snapshot}
112
+ [Console]::Error.WriteLine('TOTEM_ACL_RESULT_SENT')
113
+ } catch {
114
+ Send-Frame @{id=$request.id;error='ACL inspection failed'}
115
+ }
116
+ }
117
+ } finally { $pipe.Dispose() }
118
+ `;
119
+
120
+ export function createWindowsAclReader(options = {}) {
121
+ let worker,
122
+ nextId = 0;
123
+ const unavailable = () => new Error("The Windows access rules could not be verified.");
124
+ function stop(current) {
125
+ if (current.stopped) return;
126
+ current.stopped = true;
127
+ if (worker === current) worker = undefined;
128
+ clearTimeout(current.idle);
129
+ clearTimeout(current.reconnect);
130
+ for (const { timer, reject } of current.pending.values()) {
131
+ clearTimeout(timer);
132
+ reject(unavailable());
133
+ }
134
+ current.pending.clear();
135
+ current.socket?.destroy();
136
+ current.child?.kill();
137
+ }
138
+ function start() {
139
+ const environment = options.environment ?? process.env;
140
+ const systemRoot = options.systemRoot ?? environment.SystemRoot ?? environment.SYSTEMROOT;
141
+ if (!systemRoot) throw new Error("The Windows system directory could not be verified.");
142
+ const name = `totem-acl-${process.pid}-${randomUUID()}`;
143
+ const nonce = randomUUID();
144
+ const current = {
145
+ pending: new Map(),
146
+ buffer: "",
147
+ decoder: new StringDecoder("utf8"),
148
+ nonce,
149
+ stopped: false,
150
+ authenticated: false,
151
+ };
152
+ worker = current;
153
+ const child = (options.spawn ?? spawn)(
154
+ join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe"),
155
+ [
156
+ "-NoProfile",
157
+ "-NonInteractive",
158
+ "-EncodedCommand",
159
+ Buffer.from(inspectionScript(name), "utf16le").toString("base64"),
160
+ ],
161
+ {
162
+ // The encoded command uses a protected native pipe independently of stdin;
163
+ // no file paths or secrets are written to stdio.
164
+ stdio: ["ignore", "ignore", "pipe"],
165
+ windowsHide: true,
166
+ env: {
167
+ ...Object.fromEntries(
168
+ Object.entries(environment).filter(([name]) =>
169
+ /^(PATH|PATHEXT|COMSPEC|TEMP|TMP|USERPROFILE|HOMEDRIVE|HOMEPATH|APPDATA|LOCALAPPDATA|PROGRAMDATA|PROGRAMFILES|PROGRAMFILES\(X86\)|PSMODULEPATH|SYSTEMDRIVE|USERNAME|USERDOMAIN)$/i.test(
170
+ name,
171
+ ),
172
+ ),
173
+ ),
174
+ SystemRoot: systemRoot,
175
+ windir: systemRoot,
176
+ TOTEM_ACL_CHALLENGE: nonce,
177
+ },
178
+ },
179
+ );
180
+ current.child = child;
181
+ child.unref();
182
+ child.stderr?.unref?.();
183
+ let diagnostics = "";
184
+ child.stderr?.on("data", (chunk) => {
185
+ diagnostics = (diagnostics + chunk.toString()).slice(-4096);
186
+ for (const line of diagnostics.split(/\r?\n/).slice(0, -1))
187
+ if (/^TOTEM_ACL_[A-Z_]+$/.test(line)) options.onDiagnostic?.(line);
188
+ diagnostics = diagnostics.slice(diagnostics.lastIndexOf("\n") + 1);
189
+ });
190
+ child.on("error", () => stop(current));
191
+ child.on("close", () => stop(current));
192
+ const connect = () => {
193
+ if (current.stopped) return;
194
+ const socket = (options.createConnection ?? createConnection)({
195
+ path: "\\\\.\\pipe\\" + name,
196
+ });
197
+ current.socket = socket;
198
+ // Pending request timers keep the owner alive. Owner exit closes the pipe
199
+ // and the native reader exits; idle inspectors cannot prolong a CLI run.
200
+ socket.unref();
201
+ let retry = false;
202
+ socket.on("error", (error) => {
203
+ if (/^[A-Z_]{1,40}$/.test(error.code ?? "")) options.onDiagnostic?.(error.code);
204
+ if (!current.authenticated && error.code === "ENOENT") {
205
+ retry = true;
206
+ current.reconnect = setTimeout(connect, 25);
207
+ current.reconnect.unref();
208
+ } else stop(current);
209
+ });
210
+ socket.on("close", () => {
211
+ if (!retry) stop(current);
212
+ });
213
+ socket.on("data", (chunk) => {
214
+ if (current.stopped) return;
215
+ current.buffer += current.decoder.write(chunk);
216
+ if (Buffer.byteLength(current.buffer) > 262144) return stop(current);
217
+ let end;
218
+ while ((end = current.buffer.indexOf("\n")) >= 0) {
219
+ const line = current.buffer.slice(0, end).replace(/^\uFEFF/, "");
220
+ current.buffer = current.buffer.slice(end + 1);
221
+ let response;
222
+ try {
223
+ response = JSON.parse(line);
224
+ } catch {
225
+ return stop(current);
226
+ }
227
+ if (!current.authenticated) {
228
+ if (
229
+ response.id !== 0 ||
230
+ typeof response.nonce !== "string" ||
231
+ response.nonce.length !== nonce.length ||
232
+ !/^[a-f0-9-]{36}$/.test(response.nonce) ||
233
+ !timingSafeEqual(Buffer.from(response.nonce), Buffer.from(nonce))
234
+ )
235
+ return stop(current);
236
+ current.authenticated = true;
237
+ options.onDiagnostic?.("AUTHENTICATED");
238
+ for (const pending of current.pending.values()) socket.write(pending.request);
239
+ options.onDiagnostic?.("REQUEST_SENT");
240
+ continue;
241
+ }
242
+ const pending = current.pending.get(response.id);
243
+ if (!pending) return stop(current);
244
+ clearTimeout(pending.timer);
245
+ current.pending.delete(response.id);
246
+ options.onDiagnostic?.("RESULT_RECEIVED");
247
+ if (response.error || !response.snapshot) pending.reject(unavailable());
248
+ else pending.resolve(response.snapshot);
249
+ }
250
+ if (!current.pending.size) {
251
+ current.idle = setTimeout(() => stop(current), options.idleMs ?? 30000);
252
+ current.idle.unref();
253
+ }
254
+ });
255
+ };
256
+ connect();
257
+ return current;
258
+ }
259
+ return {
260
+ read(path) {
261
+ const encodedPath = Buffer.from(resolve(path), "utf8").toString("base64");
262
+ const current = worker ?? start();
263
+ clearTimeout(current.idle);
264
+ if (current.pending.size >= 1024) return Promise.reject(unavailable());
265
+ const id = ++nextId;
266
+ return new Promise((resolveRequest, reject) => {
267
+ const timer = setTimeout(() => stop(current), options.timeoutMs ?? 10000);
268
+ const request = JSON.stringify({ id, path: encodedPath }) + "\n";
269
+ current.pending.set(id, { resolve: resolveRequest, reject, timer, request });
270
+ if (current.authenticated) current.socket.write(request);
271
+ });
272
+ },
273
+ close() {
274
+ if (worker) stop(worker);
275
+ },
276
+ };
277
+ }
278
+
279
+ const reader = createWindowsAclReader();
57
280
  export async function assertWindowsPrivatePath(path) {
58
281
  if (process.platform !== "win32") return;
59
282
  await lstat(path); // Preserve ENOENT for callers that create a new private file.
60
- const encoded = Buffer.from(resolve(path), "utf8").toString("base64");
61
- const script = `$ErrorActionPreference='Stop'
62
- $path=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encoded}'))
63
- $sid=[Security.Principal.WindowsIdentity]::GetCurrent().User.Value
64
- function Rules($acl) { @($acl.GetAccessRules($true,$true,[Security.Principal.SecurityIdentifier]) | ForEach-Object { @{sid=$_.IdentityReference.Value;rights=[int64]$_.FileSystemRights;type=$_.AccessControlType.ToString();inheritOnly=[bool]($_.PropagationFlags -band [Security.AccessControl.PropagationFlags]::InheritOnly)} }) }
65
- $item=Get-Item -LiteralPath $path -Force
66
- $acl=Get-Acl -LiteralPath $path
67
- $parents=@(); $parent=Split-Path -LiteralPath $path
68
- while ($parent) {
69
- $p=Get-Item -LiteralPath $parent -Force
70
- $parentAcl=Get-Acl -LiteralPath $parent
71
- $parents+=@{owner=$parentAcl.GetOwner([Security.Principal.SecurityIdentifier]).Value;reparse=[bool]($p.Attributes -band [IO.FileAttributes]::ReparsePoint);rules=@(Rules $parentAcl)}
72
- $next=Split-Path -LiteralPath $parent
73
- if ($next -eq $parent) { break }; $parent=$next
74
- }
75
- @{sid=$sid;owner=$acl.GetOwner([Security.Principal.SecurityIdentifier]).Value;reparse=[bool]($item.Attributes -band [IO.FileAttributes]::ReparsePoint);rules=@(Rules $acl);ancestors=$parents} | ConvertTo-Json -Depth 8 -Compress`;
76
- const systemRoot = process.env.SystemRoot ?? process.env.SYSTEMROOT;
77
- if (!systemRoot) throw new Error("The Windows system directory could not be verified.");
78
- const { stdout } = await execute(
79
- join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe"),
80
- [
81
- "-NoProfile",
82
- "-NonInteractive",
83
- "-EncodedCommand",
84
- Buffer.from(script, "utf16le").toString("base64"),
85
- ],
86
- { timeout: 10000, maxBuffer: 262144, windowsHide: true },
87
- );
88
- validateWindowsPrivateAcl(JSON.parse(stdout.replace(/^\uFEFF/, "")));
283
+ validateWindowsPrivateAcl(await reader.read(path));
89
284
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "totemlayer",
3
- "version": "0.1.97",
3
+ "version": "0.1.115",
4
4
  "description": "Connect AI agents to Totem Network for discovery, messaging, and shared work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,7 +34,7 @@
34
34
  "homepage": "https://totemlayer.com",
35
35
  "repository": {
36
36
  "type": "git",
37
- "url": "git+https://github.com/totemlayer/Totem.git"
37
+ "url": "git+https://github.com/linbrandon4/Totem.git"
38
38
  },
39
39
  "license": "SEE LICENSE IN LICENSE.md",
40
40
  "publishConfig": {