@impractical-ai/motion 0.1.4 → 0.1.6
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 +18 -6
- package/assets/engine.d.ts +38 -2
- package/assets/impractical/SKILL.md +7 -5
- package/dist/{api-64X3G4M2.js → api-C7KJDYN4.js} +4 -2
- package/dist/{chunk-KC6BJJXH.js → chunk-BUHNO2W2.js} +335 -8
- package/dist/{chunk-M77TS5RO.js → chunk-KITRCE33.js} +68 -17
- package/dist/{chunk-EFP4P3WA.js → chunk-LJ4YH3YJ.js} +4 -3
- package/dist/{commands-WYBUEYA6.js → commands-P5CDHPDP.js} +33 -10
- package/dist/index.js +8 -8
- package/dist/{stdio-JCRFEHDW.js → stdio-GHDI4S7P.js} +206 -41
- package/package.json +4 -3
|
@@ -2,10 +2,19 @@
|
|
|
2
2
|
import {
|
|
3
3
|
PRODUCTION_APP_URL,
|
|
4
4
|
PRODUCTION_SUPABASE_URL
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-LJ4YH3YJ.js";
|
|
6
6
|
|
|
7
7
|
// src/config.ts
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
chmodSync,
|
|
10
|
+
existsSync,
|
|
11
|
+
mkdirSync,
|
|
12
|
+
readFileSync,
|
|
13
|
+
renameSync,
|
|
14
|
+
unlinkSync,
|
|
15
|
+
writeFileSync
|
|
16
|
+
} from "node:fs";
|
|
17
|
+
import { randomBytes } from "node:crypto";
|
|
9
18
|
import { homedir } from "node:os";
|
|
10
19
|
import { join } from "node:path";
|
|
11
20
|
var configDir = () => join(homedir(), ".impractical");
|
|
@@ -44,7 +53,7 @@ function readStoredConfig() {
|
|
|
44
53
|
if (error instanceof SyntaxError) {
|
|
45
54
|
throw new Error(
|
|
46
55
|
`Invalid JSON in ${configPath()}: ${error.message}
|
|
47
|
-
Run: npx --yes @impractical-ai/motion setup --
|
|
56
|
+
Run: npx --yes @impractical-ai/motion setup --agent`
|
|
48
57
|
);
|
|
49
58
|
}
|
|
50
59
|
throw new Error(
|
|
@@ -63,18 +72,34 @@ function loadConfig() {
|
|
|
63
72
|
function saveConfig(patch) {
|
|
64
73
|
const stored = readStoredConfig();
|
|
65
74
|
const next = { ...stored, ...patch };
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
const directory = configDir();
|
|
76
|
+
const path = configPath();
|
|
77
|
+
mkdirSync(directory, { recursive: true, mode: 448 });
|
|
78
|
+
chmodSync(directory, 448);
|
|
79
|
+
const temporary = join(
|
|
80
|
+
directory,
|
|
81
|
+
`.config-${process.pid}-${randomBytes(8).toString("hex")}.tmp`
|
|
82
|
+
);
|
|
83
|
+
try {
|
|
84
|
+
writeFileSync(temporary, JSON.stringify(next, null, 2) + "\n", {
|
|
85
|
+
encoding: "utf8",
|
|
86
|
+
flag: "wx",
|
|
87
|
+
mode: 384
|
|
88
|
+
});
|
|
89
|
+
chmodSync(temporary, 384);
|
|
90
|
+
renameSync(temporary, path);
|
|
91
|
+
} finally {
|
|
92
|
+
if (existsSync(temporary)) unlinkSync(temporary);
|
|
93
|
+
}
|
|
69
94
|
return next;
|
|
70
95
|
}
|
|
71
96
|
function requireConfig() {
|
|
72
97
|
const config = loadConfig();
|
|
73
98
|
if (!config.supabaseUrl) {
|
|
74
|
-
throw new Error("Not configured. Run: npx --yes @impractical-ai/motion setup --
|
|
99
|
+
throw new Error("Not configured. Run: npx --yes @impractical-ai/motion setup --agent");
|
|
75
100
|
}
|
|
76
101
|
if (!config.apiKey) {
|
|
77
|
-
throw new Error("Not connected. Run: npx --yes @impractical-ai/motion setup --
|
|
102
|
+
throw new Error("Not connected. Run: npx --yes @impractical-ai/motion setup --agent");
|
|
78
103
|
}
|
|
79
104
|
return config;
|
|
80
105
|
}
|
|
@@ -97,6 +122,16 @@ var ApiContractError = class extends Error {
|
|
|
97
122
|
this.fn = fn;
|
|
98
123
|
}
|
|
99
124
|
};
|
|
125
|
+
var ApiTransportError = class extends Error {
|
|
126
|
+
constructor(fn, endpoint, reason) {
|
|
127
|
+
super(
|
|
128
|
+
`Could not reach ${endpoint} for ${fn}. Cause: ${reason}. Check your network and IMPRACTICAL_SUPABASE_URL, then retry. If this persists, run \`npx --yes @impractical-ai/motion setup --agent\`.`
|
|
129
|
+
);
|
|
130
|
+
this.fn = fn;
|
|
131
|
+
this.endpoint = endpoint;
|
|
132
|
+
this.reason = reason;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
100
135
|
function isRecord(value) {
|
|
101
136
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
102
137
|
}
|
|
@@ -156,15 +191,30 @@ async function invoke(name, body = {}) {
|
|
|
156
191
|
return invokeAt(supabaseUrl, name, body, apiKey, receipt);
|
|
157
192
|
}
|
|
158
193
|
async function invokeAt(supabaseUrl, name, body = {}, apiKey, admissionReceipt) {
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
194
|
+
const requestUrl = `${supabaseUrl.replace(/\/$/, "")}/functions/v1/${name}`;
|
|
195
|
+
let response;
|
|
196
|
+
try {
|
|
197
|
+
response = await fetch(requestUrl, {
|
|
198
|
+
method: "POST",
|
|
199
|
+
headers: {
|
|
200
|
+
...apiKey ? { authorization: `Bearer ${apiKey}` } : {},
|
|
201
|
+
...admissionReceipt ? { "x-impractical-admission": admissionReceipt } : {},
|
|
202
|
+
"content-type": "application/json"
|
|
203
|
+
},
|
|
204
|
+
body: JSON.stringify(body)
|
|
205
|
+
});
|
|
206
|
+
} catch (error) {
|
|
207
|
+
let endpoint = `configured API/functions/v1/${encodeURIComponent(name)}`;
|
|
208
|
+
try {
|
|
209
|
+
const configured = new URL(supabaseUrl);
|
|
210
|
+
endpoint = `${configured.protocol}//${configured.host}/functions/v1/${encodeURIComponent(name)}`;
|
|
211
|
+
} catch {
|
|
212
|
+
}
|
|
213
|
+
const cause = error instanceof Error ? error.cause : void 0;
|
|
214
|
+
const code = isRecord(cause) && typeof cause.code === "string" ? cause.code : void 0;
|
|
215
|
+
const reason = code && /^[A-Z0-9_]+$/.test(code) ? code : "network transport error";
|
|
216
|
+
throw new ApiTransportError(name, endpoint, reason);
|
|
217
|
+
}
|
|
168
218
|
const text = await response.text();
|
|
169
219
|
let data = null;
|
|
170
220
|
try {
|
|
@@ -223,6 +273,7 @@ export {
|
|
|
223
273
|
requireConfig,
|
|
224
274
|
ApiError,
|
|
225
275
|
ApiContractError,
|
|
276
|
+
ApiTransportError,
|
|
226
277
|
validateApiResponse,
|
|
227
278
|
invoke,
|
|
228
279
|
invokeAt,
|
|
@@ -32,7 +32,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
32
32
|
// package.json
|
|
33
33
|
var package_default = {
|
|
34
34
|
name: "@impractical-ai/motion",
|
|
35
|
-
version: "0.1.
|
|
35
|
+
version: "0.1.6",
|
|
36
36
|
private: false,
|
|
37
37
|
description: "Create and refine Impractical Motion videos from coding agents and the terminal.",
|
|
38
38
|
homepage: "https://github.com/Openpod/impractical-motion#readme",
|
|
@@ -57,15 +57,16 @@ var package_default = {
|
|
|
57
57
|
node: ">=22"
|
|
58
58
|
},
|
|
59
59
|
scripts: {
|
|
60
|
-
build: 'node ../scripts/build-engine-dts.mjs && rm -rf dist && esbuild src/index.ts --bundle --platform=node --format=esm --target=node22 --splitting --outdir=dist --external:playwright-core --banner:js="#!/usr/bin/env node" && chmod +x dist/index.js',
|
|
60
|
+
build: 'node ../scripts/verify-locked-cli-install.mjs && node ../scripts/build-engine-dts.mjs --cli-only && rm -rf dist && ./node_modules/esbuild/bin/esbuild src/index.ts --bundle --platform=node --format=esm --target=node22 --splitting --outdir=dist --external:playwright-core --banner:js="#!/usr/bin/env node" && chmod +x dist/index.js',
|
|
61
61
|
typecheck: "tsc --noEmit",
|
|
62
|
-
test: "npm run build && esbuild tests/onboarding.test.ts tests/mcp-tools.test.ts tests/mcp-frames.test.ts tests/engine-dts.test.ts tests/mcp-connect.test.ts tests/mcp-longpoll.test.ts --bundle --platform=node --format=esm --packages=external --outdir=dist --out-extension:.js=.mjs && node --test dist/onboarding.test.mjs dist/mcp-tools.test.mjs dist/mcp-frames.test.mjs dist/engine-dts.test.mjs dist/mcp-connect.test.mjs dist/mcp-longpoll.test.mjs",
|
|
62
|
+
test: "npm run typecheck && npm run build && esbuild tests/onboarding.test.ts tests/mcp-tools.test.ts tests/mcp-frames.test.ts tests/engine-dts.test.ts tests/mcp-connect.test.ts tests/mcp-longpoll.test.ts tests/mcp-agent-timeout.test.ts tests/api-hardening.test.ts --bundle --platform=node --format=esm --packages=external --outdir=dist --out-extension:.js=.mjs && node --test dist/onboarding.test.mjs dist/mcp-tools.test.mjs dist/mcp-frames.test.mjs dist/engine-dts.test.mjs dist/mcp-connect.test.mjs dist/mcp-longpoll.test.mjs dist/mcp-agent-timeout.test.mjs dist/api-hardening.test.mjs",
|
|
63
63
|
prepack: "npm run build"
|
|
64
64
|
},
|
|
65
65
|
publishConfig: {
|
|
66
66
|
access: "public"
|
|
67
67
|
},
|
|
68
68
|
devDependencies: {
|
|
69
|
+
"@types/node": "^26.1.0",
|
|
69
70
|
esbuild: "^0.25.0",
|
|
70
71
|
typescript: "^5.6.3"
|
|
71
72
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
setup
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BUHNO2W2.js";
|
|
5
5
|
import {
|
|
6
6
|
invoke,
|
|
7
7
|
invokeAt,
|
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
saveConfig,
|
|
12
12
|
studioLink,
|
|
13
13
|
waitForJobs
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-KITRCE33.js";
|
|
15
|
+
import "./chunk-LJ4YH3YJ.js";
|
|
16
16
|
|
|
17
17
|
// src/commands.ts
|
|
18
18
|
import { readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
@@ -1142,6 +1142,26 @@ function parseSceneFiles(pasted) {
|
|
|
1142
1142
|
return files;
|
|
1143
1143
|
}
|
|
1144
1144
|
|
|
1145
|
+
// ../src/shared/local-project.ts
|
|
1146
|
+
var LOCAL_PROJECT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
1147
|
+
var LOCAL_SCENE_NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
1148
|
+
function isLocalProjectId(value) {
|
|
1149
|
+
return typeof value === "string" && LOCAL_PROJECT_ID_RE.test(value);
|
|
1150
|
+
}
|
|
1151
|
+
function localProjectMarker(input) {
|
|
1152
|
+
if (!isLocalProjectId(input.projectId) || !LOCAL_SCENE_NAME_RE.test(input.sceneName)) {
|
|
1153
|
+
throw new Error("A local project requires a valid project and scene identity");
|
|
1154
|
+
}
|
|
1155
|
+
return `${JSON.stringify({
|
|
1156
|
+
projectId: input.projectId,
|
|
1157
|
+
sceneName: input.sceneName,
|
|
1158
|
+
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1159
|
+
valueProp: input.valueProp.trim(),
|
|
1160
|
+
...input.brand ? { brand: input.brand } : {}
|
|
1161
|
+
})}
|
|
1162
|
+
`;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1145
1165
|
// ../src/shared/initial-files.ts
|
|
1146
1166
|
var TITLE_TEMPLATE = TEMPLATES.find((t) => t.id === "title-card");
|
|
1147
1167
|
var BLANK_DIRECTION = TEMPLATES.find((t) => t.id === "blank").generate({});
|
|
@@ -1162,7 +1182,11 @@ function buildInitialFiles(input) {
|
|
|
1162
1182
|
}
|
|
1163
1183
|
})
|
|
1164
1184
|
};
|
|
1165
|
-
const projectMarker =
|
|
1185
|
+
const projectMarker = input.localProject ? localProjectMarker({
|
|
1186
|
+
...input.localProject,
|
|
1187
|
+
valueProp: input.valueProp,
|
|
1188
|
+
brand: input.brand?.slug
|
|
1189
|
+
}) : JSON.stringify({
|
|
1166
1190
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1167
1191
|
valueProp: input.valueProp.trim(),
|
|
1168
1192
|
...input.brand ? { brand: input.brand.slug } : {}
|
|
@@ -1193,12 +1217,11 @@ var flagList = (flags, name) => {
|
|
|
1193
1217
|
var flagBool = (flags, name) => flags[name] === true;
|
|
1194
1218
|
var out = (line) => console.log(line);
|
|
1195
1219
|
async function setup2(flags) {
|
|
1196
|
-
const claude = flagBool(flags, "claude")
|
|
1197
|
-
|
|
1198
|
-
out("Note: --agent is deprecated; use --claude next time.");
|
|
1199
|
-
}
|
|
1220
|
+
const claude = flagBool(flags, "claude");
|
|
1221
|
+
const agent = flagBool(flags, "agent");
|
|
1200
1222
|
await setup({
|
|
1201
|
-
|
|
1223
|
+
installAgentMcp: agent,
|
|
1224
|
+
installAgentSkills: agent,
|
|
1202
1225
|
installClaudePlugin: claude,
|
|
1203
1226
|
openBrowser: !flagBool(flags, "no-open"),
|
|
1204
1227
|
log: out
|
|
@@ -1214,7 +1237,7 @@ async function login(flags) {
|
|
|
1214
1237
|
if (appUrl) patch.appUrl = appUrl;
|
|
1215
1238
|
const config = { ...loadConfig(), ...patch };
|
|
1216
1239
|
if (!config.apiKey || !config.supabaseUrl) {
|
|
1217
|
-
out("Missing API key. Run: npx --yes @impractical-ai/motion setup --
|
|
1240
|
+
out("Missing API key. Run: npx --yes @impractical-ai/motion setup --agent");
|
|
1218
1241
|
process.exitCode = 1;
|
|
1219
1242
|
return;
|
|
1220
1243
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
PACKAGE_VERSION
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-LJ4YH3YJ.js";
|
|
5
5
|
|
|
6
6
|
// src/cli-args.ts
|
|
7
7
|
import { parseArgs } from "node:util";
|
|
@@ -46,7 +46,7 @@ var HELP = `impractical \u2014 Impractical Motion from your terminal (and your c
|
|
|
46
46
|
|
|
47
47
|
Setup
|
|
48
48
|
setup --claude Sign in and install the Claude plugin + MCP
|
|
49
|
-
setup --agent
|
|
49
|
+
setup --agent Sign in and install MCP for detected coding agents
|
|
50
50
|
login --key imk_... Manual credential override for development
|
|
51
51
|
whoami Show org and plan status
|
|
52
52
|
keys [list|revoke <id>] List or revoke agent API keys
|
|
@@ -115,7 +115,7 @@ async function runCli(args, deps) {
|
|
|
115
115
|
case "push":
|
|
116
116
|
throw new Error(
|
|
117
117
|
`\`impractical ${command}\` has been removed. Draft files are edited through the MCP server now \u2014 no working copy on disk. Connect it with:
|
|
118
|
-
npx --yes @impractical-ai/motion setup --
|
|
118
|
+
npx --yes @impractical-ai/motion setup --agent`
|
|
119
119
|
);
|
|
120
120
|
case "export":
|
|
121
121
|
commands.validateMeteredCommand("export", rest, flags);
|
|
@@ -139,15 +139,15 @@ async function runCli(args, deps) {
|
|
|
139
139
|
// src/index.ts
|
|
140
140
|
runCli(process.argv.slice(2), {
|
|
141
141
|
writeLine: (line) => console.log(line),
|
|
142
|
-
loadCommands: () => import("./commands-
|
|
143
|
-
loadApi: () => import("./api-
|
|
144
|
-
loadMcp: () => import("./stdio-
|
|
142
|
+
loadCommands: () => import("./commands-P5CDHPDP.js"),
|
|
143
|
+
loadApi: () => import("./api-C7KJDYN4.js"),
|
|
144
|
+
loadMcp: () => import("./stdio-GHDI4S7P.js")
|
|
145
145
|
}).catch(async (error) => {
|
|
146
|
-
const { ApiError } = await import("./api-
|
|
146
|
+
const { ApiError } = await import("./api-C7KJDYN4.js");
|
|
147
147
|
if (error instanceof ApiError) {
|
|
148
148
|
console.error(`Error (${error.status} from ${error.fn}): ${error.message}`);
|
|
149
149
|
if (error.status === 429) console.error("Weekly MCP allowance reached. Upgrade or wait for Monday UTC.");
|
|
150
|
-
if (error.status === 401) console.error("Reconnect with: npx --yes @impractical-ai/motion setup --
|
|
150
|
+
if (error.status === 401) console.error("Reconnect with: npx --yes @impractical-ai/motion setup --agent");
|
|
151
151
|
} else {
|
|
152
152
|
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
153
153
|
}
|