@agentuity/cli 0.1.39 → 0.1.41

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 (39) hide show
  1. package/bin/cli.ts +0 -2
  2. package/dist/agent-detection.d.ts.map +1 -1
  3. package/dist/agent-detection.js +3 -1
  4. package/dist/agent-detection.js.map +1 -1
  5. package/dist/cmd/auth/org/index.d.ts.map +1 -1
  6. package/dist/cmd/auth/org/index.js +8 -2
  7. package/dist/cmd/auth/org/index.js.map +1 -1
  8. package/dist/cmd/build/entry-generator.d.ts.map +1 -1
  9. package/dist/cmd/build/entry-generator.js +19 -0
  10. package/dist/cmd/build/entry-generator.js.map +1 -1
  11. package/dist/cmd/cloud/deploy-fork.d.ts.map +1 -1
  12. package/dist/cmd/cloud/deploy-fork.js +68 -0
  13. package/dist/cmd/cloud/deploy-fork.js.map +1 -1
  14. package/dist/cmd/cloud/queue/publish.d.ts.map +1 -1
  15. package/dist/cmd/cloud/queue/publish.js +6 -1
  16. package/dist/cmd/cloud/queue/publish.js.map +1 -1
  17. package/dist/cmd/cloud/queue/util.d.ts +2 -1
  18. package/dist/cmd/cloud/queue/util.d.ts.map +1 -1
  19. package/dist/cmd/cloud/queue/util.js +3 -2
  20. package/dist/cmd/cloud/queue/util.js.map +1 -1
  21. package/dist/cmd/cloud/stream/get.js +7 -7
  22. package/dist/cmd/cloud/stream/get.js.map +1 -1
  23. package/dist/cmd/cloud/stream/list.d.ts.map +1 -1
  24. package/dist/cmd/cloud/stream/list.js +9 -6
  25. package/dist/cmd/cloud/stream/list.js.map +1 -1
  26. package/dist/utils/deps.d.ts +51 -0
  27. package/dist/utils/deps.d.ts.map +1 -1
  28. package/dist/utils/deps.js +124 -1
  29. package/dist/utils/deps.js.map +1 -1
  30. package/package.json +6 -6
  31. package/src/agent-detection.ts +3 -1
  32. package/src/cmd/auth/org/index.ts +8 -2
  33. package/src/cmd/build/entry-generator.ts +19 -0
  34. package/src/cmd/cloud/deploy-fork.ts +75 -0
  35. package/src/cmd/cloud/queue/publish.ts +7 -1
  36. package/src/cmd/cloud/queue/util.ts +3 -2
  37. package/src/cmd/cloud/stream/get.ts +7 -7
  38. package/src/cmd/cloud/stream/list.ts +9 -6
  39. package/src/utils/deps.ts +164 -1
@@ -1,4 +1,5 @@
1
1
  import { $ } from 'bun';
2
+ import { join } from 'node:path';
2
3
  export async function extractDependencies(projectDir, logger) {
3
4
  try {
4
5
  logger.debug('Extracting dependencies using bun pm ls --all');
@@ -8,7 +9,13 @@ export async function extractDependencies(projectDir, logger) {
8
9
  return [];
9
10
  }
10
11
  const output = result.stdout.toString();
11
- const packages = parseBunPmLsOutput(output);
12
+ let packages = parseBunPmLsOutput(output);
13
+ // Load alias map from bun.lock to resolve npm aliases to actual package names
14
+ const aliasMap = await loadAliasMap(projectDir, logger);
15
+ if (aliasMap.size > 0) {
16
+ logger.debug('Loaded %d package aliases from bun.lock', aliasMap.size);
17
+ packages = resolveAliases(packages, aliasMap, logger);
18
+ }
12
19
  logger.debug('Extracted %d unique packages', packages.length);
13
20
  return packages;
14
21
  }
@@ -33,4 +40,120 @@ export function parseBunPmLsOutput(output) {
33
40
  }
34
41
  return Array.from(packages.values());
35
42
  }
43
+ /**
44
+ * Load alias mappings from bun.lock file.
45
+ *
46
+ * The bun.lock file contains a "packages" object where:
47
+ * - Keys are the package names as they appear in node_modules (alias names for aliased packages)
48
+ * - Values are arrays where the first element is "actualPackageName@version"
49
+ *
50
+ * For npm aliases like `"tailwind-merge-v2": "npm:tailwind-merge@2.6.0"`:
51
+ * - Key: "tailwind-merge-v2"
52
+ * - Value[0]: "tailwind-merge@2.6.0"
53
+ *
54
+ * This function builds a map from "aliasName@version" to the actual PackageRef.
55
+ */
56
+ export async function loadAliasMap(projectDir, logger) {
57
+ const aliasMap = new Map();
58
+ try {
59
+ const lockfilePath = join(projectDir, 'bun.lock');
60
+ const lockfile = Bun.file(lockfilePath);
61
+ if (!(await lockfile.exists())) {
62
+ logger.debug('No bun.lock file found, skipping alias resolution');
63
+ return aliasMap;
64
+ }
65
+ const content = await lockfile.text();
66
+ const parsed = parseBunLockFile(content);
67
+ if (!parsed || !parsed.packages) {
68
+ return aliasMap;
69
+ }
70
+ for (const [aliasName, packageInfo] of Object.entries(parsed.packages)) {
71
+ if (!Array.isArray(packageInfo) || packageInfo.length === 0) {
72
+ continue;
73
+ }
74
+ const actualPackageSpec = packageInfo[0];
75
+ if (typeof actualPackageSpec !== 'string') {
76
+ continue;
77
+ }
78
+ // Parse "actualPackageName@version" from the first element
79
+ const atIndex = actualPackageSpec.lastIndexOf('@');
80
+ if (atIndex <= 0) {
81
+ continue;
82
+ }
83
+ const actualName = actualPackageSpec.substring(0, atIndex);
84
+ const actualVersion = actualPackageSpec.substring(atIndex + 1);
85
+ // Only add to alias map if the alias name differs from the actual name
86
+ // This indicates an npm alias (e.g., tailwind-merge-v2 -> tailwind-merge)
87
+ if (aliasName !== actualName) {
88
+ const aliasKey = `${aliasName}@${actualVersion}`;
89
+ aliasMap.set(aliasKey, { name: actualName, version: actualVersion });
90
+ }
91
+ }
92
+ }
93
+ catch (error) {
94
+ logger.debug('Failed to parse bun.lock for alias resolution: %s', error);
95
+ }
96
+ return aliasMap;
97
+ }
98
+ /**
99
+ * Parse bun.lock file content.
100
+ *
101
+ * bun.lock uses a relaxed JSON format (JSONC) with trailing commas, which
102
+ * standard JSON.parse cannot handle. We need to strip trailing commas before parsing.
103
+ *
104
+ * Structure:
105
+ * {
106
+ * "lockfileVersion": 1,
107
+ * "packages": {
108
+ * "package-name": ["actual-package@version", "", {}, "sha512-..."],
109
+ * ...
110
+ * }
111
+ * }
112
+ */
113
+ export function parseBunLockFile(content) {
114
+ try {
115
+ // bun.lock uses JSONC format with trailing commas - strip them before parsing
116
+ const sanitized = stripTrailingCommas(content);
117
+ return JSON.parse(sanitized);
118
+ }
119
+ catch {
120
+ return null;
121
+ }
122
+ }
123
+ /**
124
+ * Strip trailing commas from JSONC content to make it valid JSON.
125
+ * Handles trailing commas before ] and } characters.
126
+ */
127
+ function stripTrailingCommas(content) {
128
+ // Match comma followed by optional whitespace and then ] or }
129
+ return content.replace(/,(\s*[}\]])/g, '$1');
130
+ }
131
+ /**
132
+ * Resolve npm aliases in the package list to their actual package names.
133
+ *
134
+ * This prevents false positives in malware detection where alias names
135
+ * (e.g., "tailwind-merge-v2") are flagged as suspicious when they're
136
+ * actually legitimate aliases for real packages (e.g., "tailwind-merge").
137
+ */
138
+ export function resolveAliases(packages, aliasMap, logger) {
139
+ const resolved = new Map();
140
+ for (const pkg of packages) {
141
+ const aliasKey = `${pkg.name}@${pkg.version}`;
142
+ const actualPkg = aliasMap.get(aliasKey);
143
+ if (actualPkg) {
144
+ logger.debug('Resolved npm alias: %s@%s -> %s@%s', pkg.name, pkg.version, actualPkg.name, actualPkg.version);
145
+ const resolvedKey = `${actualPkg.name}@${actualPkg.version}`;
146
+ if (!resolved.has(resolvedKey)) {
147
+ resolved.set(resolvedKey, actualPkg);
148
+ }
149
+ }
150
+ else {
151
+ const key = `${pkg.name}@${pkg.version}`;
152
+ if (!resolved.has(key)) {
153
+ resolved.set(key, pkg);
154
+ }
155
+ }
156
+ }
157
+ return Array.from(resolved.values());
158
+ }
36
159
  //# sourceMappingURL=deps.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"deps.js","sourceRoot":"","sources":["../../src/utils/deps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACxC,UAAkB,EAClB,MAAc;IAEd,IAAI,CAAC;QACJ,MAAM,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;QAE9D,MAAM,MAAM,GAAG,MAAM,CAAC,CAAA,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,CAAC;QAE1E,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CACV,+DAA+D,EAC/D,MAAM,CAAC,QAAQ,CACf,CAAC;YACF,OAAO,EAAE,CAAC;QACX,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxC,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAE5C,MAAM,CAAC,KAAK,CAAC,8BAA8B,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC9D,OAAO,QAAQ,CAAC;IACjB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,oCAAoC,EAAE,KAAK,CAAC,CAAC;QACzD,OAAO,EAAE,CAAC;IACX,CAAC;AACF,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAChD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC/C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAC3D,IAAI,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACzB,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,OAAO,EAAE,CAAC;YACjC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YACtC,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;AACtC,CAAC"}
1
+ {"version":3,"file":"deps.js","sourceRoot":"","sources":["../../src/utils/deps.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentuity/cli",
3
- "version": "0.1.39",
3
+ "version": "0.1.41",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Agentuity employees and contributors",
6
6
  "type": "module",
@@ -40,9 +40,9 @@
40
40
  "prepublishOnly": "bun run clean && bun run build"
41
41
  },
42
42
  "dependencies": {
43
- "@agentuity/auth": "0.1.39",
44
- "@agentuity/core": "0.1.39",
45
- "@agentuity/server": "0.1.39",
43
+ "@agentuity/auth": "0.1.41",
44
+ "@agentuity/core": "0.1.41",
45
+ "@agentuity/server": "0.1.41",
46
46
  "@datasert/cronjs-parser": "^1.4.0",
47
47
  "@terascope/fetch-github-release": "^2.2.1",
48
48
  "@vitejs/plugin-react": "^5.1.2",
@@ -60,10 +60,10 @@
60
60
  "typescript": "^5.9.0",
61
61
  "vite": "^7.2.7",
62
62
  "zod": "^4.3.5",
63
- "@agentuity/frontend": "0.1.39"
63
+ "@agentuity/frontend": "0.1.41"
64
64
  },
65
65
  "devDependencies": {
66
- "@agentuity/test-utils": "0.1.39",
66
+ "@agentuity/test-utils": "0.1.41",
67
67
  "@types/adm-zip": "^0.5.7",
68
68
  "@types/bun": "latest",
69
69
  "@types/tar-fs": "^2.0.4",
@@ -151,7 +151,9 @@ function detectParentAgent(): Promise<string | undefined> {
151
151
  return;
152
152
  }
153
153
 
154
- walkTree(ppid, 0).then(resolve).catch(() => resolve(undefined));
154
+ walkTree(ppid, 0)
155
+ .then(resolve)
156
+ .catch(() => resolve(undefined));
155
157
  });
156
158
  }
157
159
 
@@ -115,7 +115,10 @@ const currentCommand = createSubcommand({
115
115
  requires: { auth: true, apiClient: true },
116
116
  examples: [
117
117
  { command: getCommand('auth org current'), description: 'Show default organization ID' },
118
- { command: getCommand('auth org current --name'), description: 'Show default organization name' },
118
+ {
119
+ command: getCommand('auth org current --name'),
120
+ description: 'Show default organization name',
121
+ },
119
122
  { command: getCommand('auth org current --json'), description: 'Show output in JSON format' },
120
123
  ],
121
124
  schema: {
@@ -125,7 +128,10 @@ const currentCommand = createSubcommand({
125
128
  response: z
126
129
  .object({
127
130
  id: z.string().nullable().describe('The current organization ID or null if not set'),
128
- name: z.string().nullable().describe('The current organization name or null if not set or not found'),
131
+ name: z
132
+ .string()
133
+ .nullable()
134
+ .describe('The current organization name or null if not set or not found'),
129
135
  })
130
136
  .describe('The current organization details'),
131
137
  },
@@ -84,6 +84,7 @@ export async function generateEntryFile(options: GenerateEntryOptions): Promise<
84
84
  ` createWorkbenchRouter,`,
85
85
  ` bootstrapRuntimeEnv,`,
86
86
  ` patchBunS3ForStorageDev,`,
87
+ ` runShutdown,`,
87
88
  ];
88
89
 
89
90
  const imports = [
@@ -377,6 +378,24 @@ if (typeof Bun !== 'undefined') {
377
378
  if (isDevelopment() && process.env.VITE_PORT) {
378
379
  otel.logger.debug(\`Proxying Vite assets from port \${process.env.VITE_PORT}\`);
379
380
  }
381
+
382
+ // Register signal handlers for graceful shutdown (production only)
383
+ // Dev mode has its own handlers in devmode.ts
384
+ if (!isDevelopment()) {
385
+ const handleShutdown = async (signal: string) => {
386
+ otel.logger.info(\`Received \${signal}, initiating graceful shutdown...\`);
387
+ try {
388
+ await runShutdown();
389
+ otel.logger.info('Shutdown complete');
390
+ } catch (err) {
391
+ otel.logger.error(\`Error during shutdown: \${err instanceof Error ? err.message : String(err)}\`);
392
+ }
393
+ process.exit(0);
394
+ };
395
+
396
+ process.once('SIGTERM', () => handleShutdown('SIGTERM'));
397
+ process.once('SIGINT', () => handleShutdown('SIGINT'));
398
+ }
380
399
  }
381
400
 
382
401
  // FOUND AN ERROR IN THIS FILE?
@@ -75,6 +75,77 @@ export async function runForkedDeploy(options: ForkDeployOptions): Promise<ForkD
75
75
  const cleanLogsFile = join(tmpdir(), `agentuity-deploy-${deploymentId}-logs.txt`);
76
76
  let outputBuffer = '';
77
77
  let proc: Subprocess | null = null;
78
+ let cancelled = false;
79
+
80
+ // Signal handler to forward signals to child process and report cancellation
81
+ const handleSignal = async (signal: NodeJS.Signals) => {
82
+ if (cancelled) return;
83
+ cancelled = true;
84
+
85
+ logger.debug('Received %s, forwarding to child process', signal);
86
+
87
+ // Kill the child process if it's still running
88
+ if (proc && proc.exitCode === null) {
89
+ try {
90
+ proc.kill(signal);
91
+ } catch (err) {
92
+ logger.debug('Failed to kill child process: %s', err);
93
+ }
94
+ }
95
+
96
+ // Report deployment as cancelled (with timeout to ensure prompt exit)
97
+ const cancelMessage = 'Deployment cancelled by user';
98
+ const timeoutMs = 3000; // 3 second timeout
99
+ const timeoutPromise = new Promise<void>((resolve) => {
100
+ setTimeout(() => {
101
+ logger.debug('API call to report cancellation timed out after %dms', timeoutMs);
102
+ resolve();
103
+ }, timeoutMs);
104
+ });
105
+
106
+ const apiCallPromise = projectDeploymentFail(apiClient, deploymentId, {
107
+ error: cancelMessage,
108
+ diagnostics: {
109
+ success: false,
110
+ errors: [
111
+ {
112
+ type: 'general',
113
+ scope: 'deploy',
114
+ message: cancelMessage,
115
+ code: 'DEPLOY_CANCELLED',
116
+ },
117
+ ],
118
+ warnings: [],
119
+ diagnostics: [],
120
+ error: cancelMessage,
121
+ },
122
+ }).catch((err) => {
123
+ logger.debug('Failed to report cancellation: %s', err);
124
+ });
125
+
126
+ // Race API call against timeout to ensure prompt exit
127
+ await Promise.race([apiCallPromise, timeoutPromise]);
128
+
129
+ // Exit with signal-specific exit code
130
+ const signalExitCodes: Record<string, number> = {
131
+ SIGINT: 130, // 128 + 2
132
+ SIGTERM: 143, // 128 + 15
133
+ SIGHUP: 129, // 128 + 1
134
+ SIGQUIT: 131, // 128 + 3
135
+ };
136
+ const exitCode = signalExitCodes[signal] ?? 128;
137
+ process.exit(exitCode);
138
+ };
139
+
140
+ // Install signal handlers
141
+ const sigintHandler = () => {
142
+ void handleSignal('SIGINT');
143
+ };
144
+ const sigtermHandler = () => {
145
+ void handleSignal('SIGTERM');
146
+ };
147
+ process.on('SIGINT', sigintHandler);
148
+ process.on('SIGTERM', sigtermHandler);
78
149
 
79
150
  try {
80
151
  const childArgs = [
@@ -282,6 +353,10 @@ export async function runForkedDeploy(options: ForkDeployOptions): Promise<ForkD
282
353
  },
283
354
  };
284
355
  } finally {
356
+ // Clean up signal handlers
357
+ process.off('SIGINT', sigintHandler);
358
+ process.off('SIGTERM', sigtermHandler);
359
+
285
360
  // Clean up temp files
286
361
  for (const file of [reportFile, cleanLogsFile]) {
287
362
  if (existsSync(file)) {
@@ -38,6 +38,7 @@ export const publishSubcommand = createCommand({
38
38
  partitionKey: z.string().optional().describe('Partition key for ordering'),
39
39
  idempotencyKey: z.string().optional().describe('Idempotency key to prevent duplicates'),
40
40
  ttl: z.coerce.number().optional().describe('Message TTL in seconds'),
41
+ sync: z.boolean().optional().describe('Publish synchronously (wait for persistence)'),
41
42
  }),
42
43
  response: MessageSchema,
43
44
  },
@@ -62,6 +63,11 @@ export const publishSubcommand = createCommand({
62
63
  }
63
64
  }
64
65
 
66
+ const apiOptions = getQueueApiOptions(ctx) ?? {};
67
+ if (opts.sync) {
68
+ apiOptions.sync = true;
69
+ }
70
+
65
71
  const message = await publishMessage(
66
72
  client,
67
73
  args.queue_name,
@@ -72,7 +78,7 @@ export const publishSubcommand = createCommand({
72
78
  idempotency_key: opts.idempotencyKey,
73
79
  ttl_seconds: opts.ttl,
74
80
  },
75
- getQueueApiOptions(ctx)
81
+ apiOptions
76
82
  );
77
83
 
78
84
  if (!options.json) {
@@ -26,9 +26,10 @@ export async function createQueueAPIClient(ctx: QueueContext): Promise<APIClient
26
26
 
27
27
  /**
28
28
  * Creates QueueApiOptions from the CLI context.
29
- * Prioritizes explicit orgId on context, then falls back to global --org-id option.
29
+ * Prioritizes explicit orgId on context, then falls back to global --org-id option,
30
+ * and finally to the preferred org from the profile configuration.
30
31
  */
31
32
  export function getQueueApiOptions(ctx: QueueContext): QueueApiOptions | undefined {
32
- const orgId = ctx.orgId ?? ctx.options.orgId;
33
+ const orgId = ctx.orgId ?? ctx.options.orgId ?? ctx.config?.preferences?.orgId;
33
34
  return orgId ? { orgId } : undefined;
34
35
  }
@@ -6,7 +6,7 @@ import { getCommand } from '../../../command-prefix';
6
6
 
7
7
  const GetStreamResponseSchema = z.object({
8
8
  id: z.string().describe('Stream ID'),
9
- name: z.string().describe('Stream name'),
9
+ namespace: z.string().describe('Stream namespace'),
10
10
  metadata: z.record(z.string(), z.string()).describe('Stream metadata'),
11
11
  url: z.string().describe('Public URL'),
12
12
  sizeBytes: z.number().describe('Size in bytes'),
@@ -70,7 +70,7 @@ export const getSubcommand = createCommand({
70
70
  const stream = await storage.get(args.id);
71
71
  return {
72
72
  id: args.id,
73
- name: stream.name ?? '',
73
+ namespace: stream.namespace ?? '',
74
74
  metadata: stream.metadata ?? {},
75
75
  url: stream.url ?? '',
76
76
  sizeBytes: stats.size,
@@ -91,10 +91,10 @@ export const getSubcommand = createCommand({
91
91
 
92
92
  const sizeBytes = stream.sizeBytes ?? 0;
93
93
 
94
- console.log(`Name: ${tui.bold(stream.name ?? 'unknown')}`);
95
- console.log(`ID: ${stream.id}`);
96
- console.log(`Size: ${tui.formatBytes(sizeBytes)}`);
97
- console.log(`URL: ${tui.link(stream.url ?? 'unknown')}`);
94
+ console.log(`Namespace: ${tui.bold(stream.namespace ?? 'unknown')}`);
95
+ console.log(`ID: ${stream.id}`);
96
+ console.log(`Size: ${tui.formatBytes(sizeBytes)}`);
97
+ console.log(`URL: ${tui.link(stream.url ?? 'unknown')}`);
98
98
  if (stream.metadata && Object.keys(stream.metadata).length > 0) {
99
99
  console.log(`Metadata:`);
100
100
  for (const [key, value] of Object.entries(stream.metadata)) {
@@ -106,7 +106,7 @@ export const getSubcommand = createCommand({
106
106
 
107
107
  return {
108
108
  id: stream.id,
109
- name: stream.name,
109
+ namespace: stream.namespace,
110
110
  metadata: stream.metadata,
111
111
  url: stream.url,
112
112
  sizeBytes: stream.sizeBytes,
@@ -6,7 +6,7 @@ import { getCommand } from '../../../command-prefix';
6
6
 
7
7
  const StreamInfoSchema = z.object({
8
8
  id: z.string().describe('Stream ID'),
9
- name: z.string().describe('Stream name'),
9
+ namespace: z.string().describe('Stream namespace'),
10
10
  metadata: z.record(z.string(), z.string()).describe('Stream metadata'),
11
11
  url: z.string().describe('Public URL'),
12
12
  sizeBytes: z.number().describe('Size in bytes'),
@@ -30,7 +30,10 @@ export const listSubcommand = createCommand({
30
30
  command: getCommand('cloud stream ls --size 50'),
31
31
  description: 'List 50 most recent streams',
32
32
  },
33
- { command: getCommand('cloud stream list --name agent-logs'), description: 'Filter by name' },
33
+ {
34
+ command: getCommand('cloud stream list --namespace agent-logs'),
35
+ description: 'Filter by namespace',
36
+ },
34
37
  {
35
38
  command: getCommand('cloud stream list --metadata type=export'),
36
39
  description: 'Filter by metadata',
@@ -41,7 +44,7 @@ export const listSubcommand = createCommand({
41
44
  options: z.object({
42
45
  size: z.number().optional().describe('maximum number of streams to return (default: 100)'),
43
46
  offset: z.number().optional().describe('number of streams to skip for pagination'),
44
- name: z.string().optional().describe('filter by stream name'),
47
+ namespace: z.string().optional().describe('filter by stream namespace'),
45
48
  metadata: z
46
49
  .string()
47
50
  .optional()
@@ -95,7 +98,7 @@ export const listSubcommand = createCommand({
95
98
  const result = await storage.list({
96
99
  limit: opts.size,
97
100
  offset: opts.offset,
98
- name: opts.name,
101
+ namespace: opts.namespace,
99
102
  metadata: metadataFilter,
100
103
  });
101
104
 
@@ -115,7 +118,7 @@ export const listSubcommand = createCommand({
115
118
  const metadataStr =
116
119
  Object.keys(stream.metadata).length > 0 ? JSON.stringify(stream.metadata) : '-';
117
120
  return {
118
- Name: stream.name,
121
+ Namespace: stream.namespace,
119
122
  ID: stream.id,
120
123
  Size: tui.formatBytes(sizeBytes),
121
124
  Metadata:
@@ -125,7 +128,7 @@ export const listSubcommand = createCommand({
125
128
  });
126
129
 
127
130
  tui.table(tableData, [
128
- { name: 'Name', alignment: 'left' },
131
+ { name: 'Namespace', alignment: 'left' },
129
132
  { name: 'ID', alignment: 'left' },
130
133
  { name: 'Size', alignment: 'right' },
131
134
  { name: 'Metadata', alignment: 'left' },
package/src/utils/deps.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { $ } from 'bun';
2
+ import { join } from 'node:path';
2
3
  import type { Logger } from '../types';
3
4
 
4
5
  export interface PackageRef {
@@ -6,6 +7,13 @@ export interface PackageRef {
6
7
  version: string;
7
8
  }
8
9
 
10
+ /**
11
+ * Mapping from alias key (aliasName@version) to actual package reference.
12
+ * Used to resolve npm aliases (e.g., "tailwind-merge-v2": "npm:tailwind-merge@2.6.0")
13
+ * to their actual package names for accurate malware detection.
14
+ */
15
+ export type AliasMap = Map<string, PackageRef>;
16
+
9
17
  export async function extractDependencies(
10
18
  projectDir: string,
11
19
  logger: Logger
@@ -24,7 +32,14 @@ export async function extractDependencies(
24
32
  }
25
33
 
26
34
  const output = result.stdout.toString();
27
- const packages = parseBunPmLsOutput(output);
35
+ let packages = parseBunPmLsOutput(output);
36
+
37
+ // Load alias map from bun.lock to resolve npm aliases to actual package names
38
+ const aliasMap = await loadAliasMap(projectDir, logger);
39
+ if (aliasMap.size > 0) {
40
+ logger.debug('Loaded %d package aliases from bun.lock', aliasMap.size);
41
+ packages = resolveAliases(packages, aliasMap, logger);
42
+ }
28
43
 
29
44
  logger.debug('Extracted %d unique packages', packages.length);
30
45
  return packages;
@@ -52,3 +67,151 @@ export function parseBunPmLsOutput(output: string): PackageRef[] {
52
67
 
53
68
  return Array.from(packages.values());
54
69
  }
70
+
71
+ /**
72
+ * Load alias mappings from bun.lock file.
73
+ *
74
+ * The bun.lock file contains a "packages" object where:
75
+ * - Keys are the package names as they appear in node_modules (alias names for aliased packages)
76
+ * - Values are arrays where the first element is "actualPackageName@version"
77
+ *
78
+ * For npm aliases like `"tailwind-merge-v2": "npm:tailwind-merge@2.6.0"`:
79
+ * - Key: "tailwind-merge-v2"
80
+ * - Value[0]: "tailwind-merge@2.6.0"
81
+ *
82
+ * This function builds a map from "aliasName@version" to the actual PackageRef.
83
+ */
84
+ export async function loadAliasMap(projectDir: string, logger: Logger): Promise<AliasMap> {
85
+ const aliasMap: AliasMap = new Map();
86
+
87
+ try {
88
+ const lockfilePath = join(projectDir, 'bun.lock');
89
+ const lockfile = Bun.file(lockfilePath);
90
+
91
+ if (!(await lockfile.exists())) {
92
+ logger.debug('No bun.lock file found, skipping alias resolution');
93
+ return aliasMap;
94
+ }
95
+
96
+ const content = await lockfile.text();
97
+ const parsed = parseBunLockFile(content);
98
+
99
+ if (!parsed || !parsed.packages) {
100
+ return aliasMap;
101
+ }
102
+
103
+ for (const [aliasName, packageInfo] of Object.entries(parsed.packages)) {
104
+ if (!Array.isArray(packageInfo) || packageInfo.length === 0) {
105
+ continue;
106
+ }
107
+
108
+ const actualPackageSpec = packageInfo[0];
109
+ if (typeof actualPackageSpec !== 'string') {
110
+ continue;
111
+ }
112
+
113
+ // Parse "actualPackageName@version" from the first element
114
+ const atIndex = actualPackageSpec.lastIndexOf('@');
115
+ if (atIndex <= 0) {
116
+ continue;
117
+ }
118
+
119
+ const actualName = actualPackageSpec.substring(0, atIndex);
120
+ const actualVersion = actualPackageSpec.substring(atIndex + 1);
121
+
122
+ // Only add to alias map if the alias name differs from the actual name
123
+ // This indicates an npm alias (e.g., tailwind-merge-v2 -> tailwind-merge)
124
+ if (aliasName !== actualName) {
125
+ const aliasKey = `${aliasName}@${actualVersion}`;
126
+ aliasMap.set(aliasKey, { name: actualName, version: actualVersion });
127
+ }
128
+ }
129
+ } catch (error) {
130
+ logger.debug('Failed to parse bun.lock for alias resolution: %s', error);
131
+ }
132
+
133
+ return aliasMap;
134
+ }
135
+
136
+ /**
137
+ * Parse bun.lock file content.
138
+ *
139
+ * bun.lock uses a relaxed JSON format (JSONC) with trailing commas, which
140
+ * standard JSON.parse cannot handle. We need to strip trailing commas before parsing.
141
+ *
142
+ * Structure:
143
+ * {
144
+ * "lockfileVersion": 1,
145
+ * "packages": {
146
+ * "package-name": ["actual-package@version", "", {}, "sha512-..."],
147
+ * ...
148
+ * }
149
+ * }
150
+ */
151
+ export function parseBunLockFile(content: string): BunLockFile | null {
152
+ try {
153
+ // bun.lock uses JSONC format with trailing commas - strip them before parsing
154
+ const sanitized = stripTrailingCommas(content);
155
+ return JSON.parse(sanitized) as BunLockFile;
156
+ } catch {
157
+ return null;
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Strip trailing commas from JSONC content to make it valid JSON.
163
+ * Handles trailing commas before ] and } characters.
164
+ */
165
+ function stripTrailingCommas(content: string): string {
166
+ // Match comma followed by optional whitespace and then ] or }
167
+ return content.replace(/,(\s*[}\]])/g, '$1');
168
+ }
169
+
170
+ /**
171
+ * Represents the structure of a bun.lock file.
172
+ */
173
+ export interface BunLockFile {
174
+ lockfileVersion?: number;
175
+ packages?: Record<string, unknown[]>;
176
+ }
177
+
178
+ /**
179
+ * Resolve npm aliases in the package list to their actual package names.
180
+ *
181
+ * This prevents false positives in malware detection where alias names
182
+ * (e.g., "tailwind-merge-v2") are flagged as suspicious when they're
183
+ * actually legitimate aliases for real packages (e.g., "tailwind-merge").
184
+ */
185
+ export function resolveAliases(
186
+ packages: PackageRef[],
187
+ aliasMap: AliasMap,
188
+ logger: Logger
189
+ ): PackageRef[] {
190
+ const resolved = new Map<string, PackageRef>();
191
+
192
+ for (const pkg of packages) {
193
+ const aliasKey = `${pkg.name}@${pkg.version}`;
194
+ const actualPkg = aliasMap.get(aliasKey);
195
+
196
+ if (actualPkg) {
197
+ logger.debug(
198
+ 'Resolved npm alias: %s@%s -> %s@%s',
199
+ pkg.name,
200
+ pkg.version,
201
+ actualPkg.name,
202
+ actualPkg.version
203
+ );
204
+ const resolvedKey = `${actualPkg.name}@${actualPkg.version}`;
205
+ if (!resolved.has(resolvedKey)) {
206
+ resolved.set(resolvedKey, actualPkg);
207
+ }
208
+ } else {
209
+ const key = `${pkg.name}@${pkg.version}`;
210
+ if (!resolved.has(key)) {
211
+ resolved.set(key, pkg);
212
+ }
213
+ }
214
+ }
215
+
216
+ return Array.from(resolved.values());
217
+ }