@swell/cli
Version:
Swell's command line interface/utility
163 lines (162 loc) • 5.5 kB
JavaScript
import { getCurrentAppSlugId } from '../apps/index.js';
import { classifyIdentifier, resolveInspectScope, } from '../apps/inspect-scope.js';
import { HEX24 } from '../apps/object-id.js';
import { resolveAppId } from '../apps/resolve.js';
export const WORKFLOW_STATUSES = [
'active',
'failed',
'completed',
'terminated',
];
export const WORKFLOW_INSTANCE_ID_PATTERN = /^wf_inst_[\dA-Za-z]+$/;
const REDACTED_WORKFLOW_FIELDS = new Set([
'access_token',
'api_key',
'authorization',
'bearer_token',
'cf_api_token',
'cloudflare_token',
'installed_app_id',
'params',
'private_key',
'public_key',
'runtime_token',
'secret_key',
'token',
'token_hash',
]);
const RELATIVE_TIME_FORMAT = new Intl.RelativeTimeFormat('en', {
numeric: 'auto',
});
export async function resolveWorkflowOperationRef(api, workflow, flags) {
const kind = classifyIdentifier(workflow);
if (kind.kind === 'slug') {
const appId = HEX24.test(kind.appPart)
? kind.appPart
: await resolveAppId(api, kind.appPart);
return {
appId,
appSlug: kind.appPart,
query: { workflow_name: kind.name },
};
}
if (kind.kind === 'id') {
const manifest = await api.get({
adminPath: `/data/:functions/${kind.id}`,
});
if (!manifest?.app_id) {
throw new Error(`Workflow manifest '${workflow}' does not have an app_id. Pass app.<app>.<workflow> or --app=<slug>.`);
}
const workflowName = workflowDisplayName(manifest);
if (workflowName === kind.id || workflowName === '-') {
throw new Error(`Workflow manifest '${workflow}' does not have a workflow name. Pass app.<app>.<workflow> or --app=<slug>.`);
}
return {
appId: manifest.app_id,
query: { workflow_id: kind.id, workflow_name: workflowName },
};
}
if (kind.kind === 'name') {
const scope = await resolveWorkflowScope(api, flags);
if (!scope.appId) {
throw new Error(`Bare workflow name '${workflow}' requires --app=<slug> or --app=. to scope. Alternatively, pass app.<app>.<workflow> or a 24-char manifest id.`);
}
return {
appId: scope.appId,
appSlug: scope.appSlug,
query: { workflow_name: workflow },
};
}
throw new Error(`Invalid workflow identifier '${workflow}'. Expected bare name, app.<app>.<workflow>, or 24-char manifest id.`);
}
export async function resolveWorkflowScope(api, flags) {
return resolveInspectScope({
resolveAppId: (slug) => resolveAppId(api, slug),
readCurrentAppSlug: () => getCurrentAppSlugId(),
}, flags);
}
export function isWorkflowInstanceIdentifier(value) {
return WORKFLOW_INSTANCE_ID_PATTERN.test(value);
}
export function workflowDisplayName(record) {
return record.workflow_name || record.name || record.id || '-';
}
export function workflowListMeta(record) {
const parts = [];
const summary = record.summary || {};
if (!record.enabled) {
parts.push('disabled');
}
if (summary.active_instances) {
parts.push(`active ${summary.active_instances}`);
}
if (summary.failed_instances) {
parts.push(`failed ${summary.failed_instances}`);
}
if (summary.completed_recent) {
parts.push(`completed ${summary.completed_recent}`);
}
if (summary.terminated_recent) {
parts.push(`terminated ${summary.terminated_recent}`);
}
return parts.join(' · ');
}
export function workflowRunDate(run) {
return (run.date_failed ||
run.date_terminated ||
run.date_completed ||
run.date_started ||
run.date_created);
}
export function workflowRunMeta(run) {
const parts = [];
if (run.status) {
parts.push(run.status);
}
const date = workflowRunDate(run);
if (date) {
parts.push(formatWorkflowRelativeTime(date));
}
return parts.join(' · ');
}
export function formatWorkflowRelativeTime(value, now = Date.now()) {
const timestamp = Date.parse(value);
if (!Number.isFinite(timestamp)) {
return value;
}
const diffSeconds = Math.round((timestamp - now) / 1000);
const absSeconds = Math.abs(diffSeconds);
if (absSeconds < 60) {
return RELATIVE_TIME_FORMAT.format(diffSeconds, 'second');
}
if (absSeconds < 60 * 60) {
return RELATIVE_TIME_FORMAT.format(Math.round(diffSeconds / 60), 'minute');
}
if (absSeconds < 24 * 60 * 60) {
return RELATIVE_TIME_FORMAT.format(Math.round(diffSeconds / (60 * 60)), 'hour');
}
if (absSeconds < 30 * 24 * 60 * 60) {
return RELATIVE_TIME_FORMAT.format(Math.round(diffSeconds / (24 * 60 * 60)), 'day');
}
return value;
}
export function redactWorkflowPayload(value) {
if (Array.isArray(value)) {
return value.map((item) => redactWorkflowPayload(item));
}
if (!value || typeof value !== 'object') {
if (typeof value === 'string' && value.startsWith('wf_rt_')) {
return '[redacted]';
}
return value;
}
const result = {};
for (const [key, child] of Object.entries(value)) {
if (REDACTED_WORKFLOW_FIELDS.has(key.toLowerCase())) {
result[key] = '[redacted]';
continue;
}
result[key] = redactWorkflowPayload(child);
}
return result;
}