@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.
- package/bin/cli.ts +0 -2
- package/dist/agent-detection.d.ts.map +1 -1
- package/dist/agent-detection.js +3 -1
- package/dist/agent-detection.js.map +1 -1
- package/dist/cmd/auth/org/index.d.ts.map +1 -1
- package/dist/cmd/auth/org/index.js +8 -2
- package/dist/cmd/auth/org/index.js.map +1 -1
- package/dist/cmd/build/entry-generator.d.ts.map +1 -1
- package/dist/cmd/build/entry-generator.js +19 -0
- package/dist/cmd/build/entry-generator.js.map +1 -1
- package/dist/cmd/cloud/deploy-fork.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy-fork.js +68 -0
- package/dist/cmd/cloud/deploy-fork.js.map +1 -1
- package/dist/cmd/cloud/queue/publish.d.ts.map +1 -1
- package/dist/cmd/cloud/queue/publish.js +6 -1
- package/dist/cmd/cloud/queue/publish.js.map +1 -1
- package/dist/cmd/cloud/queue/util.d.ts +2 -1
- package/dist/cmd/cloud/queue/util.d.ts.map +1 -1
- package/dist/cmd/cloud/queue/util.js +3 -2
- package/dist/cmd/cloud/queue/util.js.map +1 -1
- package/dist/cmd/cloud/stream/get.js +7 -7
- package/dist/cmd/cloud/stream/get.js.map +1 -1
- package/dist/cmd/cloud/stream/list.d.ts.map +1 -1
- package/dist/cmd/cloud/stream/list.js +9 -6
- package/dist/cmd/cloud/stream/list.js.map +1 -1
- package/dist/utils/deps.d.ts +51 -0
- package/dist/utils/deps.d.ts.map +1 -1
- package/dist/utils/deps.js +124 -1
- package/dist/utils/deps.js.map +1 -1
- package/package.json +6 -6
- package/src/agent-detection.ts +3 -1
- package/src/cmd/auth/org/index.ts +8 -2
- package/src/cmd/build/entry-generator.ts +19 -0
- package/src/cmd/cloud/deploy-fork.ts +75 -0
- package/src/cmd/cloud/queue/publish.ts +7 -1
- package/src/cmd/cloud/queue/util.ts +3 -2
- package/src/cmd/cloud/stream/get.ts +7 -7
- package/src/cmd/cloud/stream/list.ts +9 -6
- package/src/utils/deps.ts +164 -1
package/dist/utils/deps.js
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/utils/deps.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deps.js","sourceRoot":"","sources":["../../src/utils/deps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,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.
|
|
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.
|
|
44
|
-
"@agentuity/core": "0.1.
|
|
45
|
-
"@agentuity/server": "0.1.
|
|
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.
|
|
63
|
+
"@agentuity/frontend": "0.1.41"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@agentuity/test-utils": "0.1.
|
|
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",
|
package/src/agent-detection.ts
CHANGED
|
@@ -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
|
-
{
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(`
|
|
95
|
-
console.log(`ID:
|
|
96
|
-
console.log(`Size:
|
|
97
|
-
console.log(`URL:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
{
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: '
|
|
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
|
-
|
|
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
|
+
}
|